【问题标题】:Can you tell what is wrong with this query?你能说出这个查询有什么问题吗?
【发布时间】:2011-03-09 11:44:19
【问题描述】:

大家好

我正在使用单个数据库和大约 7 个表。确实有数据填充了所有表。 说现在大约10k左右。但会进一步增长,可能会影响数百万人,但需要时间。

我的问题是为什么我的查询获取结果很慢。查询非负载条件大约需要 10 到 12 秒。我担心在负载条件下发生的情况是否会同时显示数千个查询??

这是我的示例查询...

$result = $db->sql_query("SELECT * FROM table1,table2,table3,table4,table5 WHERE table1.url = table2.url AND table1.url = table3.url AND table1.url = table4.url AND table1.url = table5.url  AND table1.url='".$uri."'")or die(mysql_error());

$row = $db->sql_fetchrow($result);

$daysA = $row['regtime'];
$days = (strtotime(date("Y-m-d")) - strtotime($row['regtime'])) / (60 * 60 * 24);
if($row > 0 && $days < 2){

$row['data'];
$row['data1'];
//remaining 

}else{ //some code}

【问题讨论】:

  • url 字段是否被索引?什么是“tfdata.web”?
  • @Lasse V. Karlsen ....对不起,我纠正了它
  • 好的,但是改正后,你的url字段是否被索引了?
  • Yes UNIQUE INDEX....by 我如何检查??
  • 您是否从控制台(即 PHP 外部)测试了 SQL 命令,以确定它是否是 SELECT 本身的问题,还是问题出在其他地方?另外,发布SHOW CREATE TABLE table1 的结果(我假设这些表都具有相同的结构,如果不是,也可以发布其他表结构)。最后,您应该提供EXPLAIN SELECT 语句的输出。

标签: mysql


【解决方案1】:

我不确定您是否解决了问题,但这是我制作的一些测试数据。有许多因素会影响您的查询速度,因此我的简单测试用例可能无法准确反映您的表或数据。但是,它们可以作为一个有用的起点。

首先,创建 5 个简单的表,每个表都具有相同的结构。与您的表格一样,我在 url 列上使用了 UNIQUE 索引:

CREATE TABLE `table1` (
    `id` int(11) NOT NULL auto_increment,
    `url` varchar(255) default NULL,
    PRIMARY KEY  (`id`),
    UNIQUE KEY `url` (`url`)
) ENGINE=InnoDB;

CREATE TABLE table2 LIKE table1;
CREATE TABLE table3 LIKE table1;
CREATE TABLE table4 LIKE table1;
CREATE TABLE table5 LIKE table1;

以下脚本创建一个存储过程,用于在每个表中填充 10,000 行数据:

DELIMITER //
DROP PROCEDURE IF EXISTS test.autofill//
CREATE PROCEDURE test.autofill()
BEGIN
    DECLARE i INT DEFAULT 5;
    WHILE i < 10000 DO
        INSERT INTO table1 (url) VALUES (CONCAT('wwww.stackoverflow.com/', i ));
        INSERT INTO table2 (url) VALUES (CONCAT('wwww.stackoverflow.com/', 10000 - i ));
        INSERT INTO table3 (url) VALUES (CONCAT('wwww.stackoverflow.com/', i + 6000 ));
        INSERT INTO table4 (url) VALUES (CONCAT('wwww.stackoverflow.com/', i + 3000 ));
        INSERT INTO table5 (url) VALUES (CONCAT('wwww.stackoverflow.com/', i + 2000 ));
        SET i = i + 1;
    END WHILE;
END;
//
DELIMITER ;

CALL test.autofill();

现在每个表都包含 10,000 行。您的SELECT 语句现在可用于查询数据:

SELECT * 
FROM table1,table2,table3,table4,table5
WHERE table1.url = table2.url
AND table1.url = table3.url
AND table1.url = table4.url
AND table1.url = table5.url
AND table1.url = 'wwww.stackoverflow.com/8000';

这几乎立即给出了以下结果:

+------+-----------------------------+------+-----------------------------+------+-----------------------------+------+-----------------------------+------+-----------------------------+
| id   | url                         | id   | url                         | id   | url                         | id   | url                         | id   | url                         |
+------+-----------------------------+------+-----------------------------+------+-----------------------------+------+-----------------------------+------+-----------------------------+
| 7996 | wwww.stackoverflow.com/8000 | 1996 | wwww.stackoverflow.com/8000 | 1996 | wwww.stackoverflow.com/8000 | 4996 | wwww.stackoverflow.com/8000 | 5996 | wwww.stackoverflow.com/8000 |
+------+-----------------------------+------+-----------------------------+------+-----------------------------+------+-----------------------------+------+-----------------------------+

EXPLAIN SELECT 说明了查询速度非常快的原因:

EXPLAIN SELECT * 
FROM table1,table2,table3,table4,table5
WHERE table1.url = table2.url
AND table1.url = table3.url
AND table1.url = table4.url
AND table1.url = table5.url
AND table1.url = 'wwww.stackoverflow.com/8000';

+----+-------------+--------+-------+---------------+------+---------+-------+------+-------------+
| id | select_type | table  | type  | possible_keys | key  | key_len | ref   | rows | Extra       |
+----+-------------+--------+-------+---------------+------+---------+-------+------+-------------+
|  1 | SIMPLE      | table1 | const | url           | url  | 258     | const |    1 | Using index |
|  1 | SIMPLE      | table2 | const | url           | url  | 258     | const |    1 | Using index |
|  1 | SIMPLE      | table3 | const | url           | url  | 258     | const |    1 | Using index |
|  1 | SIMPLE      | table4 | const | url           | url  | 258     | const |    1 | Using index |
|  1 | SIMPLE      | table5 | const | url           | url  | 258     | const |    1 | Using index |
+----+-------------+--------+-------+---------------+------+---------+-------+------+-------------+

select_typeSIMPLE,这意味着没有JOIN 语句可以减慢速度。

typeconst,这意味着该表最多有一个可能的匹配项 - 这要归功于 UNIQUE 索引,它保证没有两个 URL 会相同(请参阅 mysql 5.0 indexes - Unique vs Non Unique UNIQUE INDEX 的描述)。 type 列中的 const 值与您所能得到的一样好。

possible_keyskey 使用 url 键。这意味着每个表都使用了正确的索引。

refconst,这意味着 MySQL 正在将一个常量值(一个不变的值)与索引进行比较。同样,这非常快。

rows 等于 1。MySQL 只需要从每个表中查看一行。再一次,这非常快。

ExtraUsing index。 MySQL 不必对表进行任何额外的非索引搜索。

如果您在每个表的 url 列上都有索引,那么您的查询应该非常快。

【讨论】:

  • 我收到此错误错误 SQL 查询:CREATE PROCEDURE test.autofill() BEGIN DECLARE i INT DEFAULT 5; MySQL 说:文档 #1064 - 你的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 3 行的 '' 附近使用正确的语法
  • 嘿,完成了......我得到了你所展示的确切结果......这意味着一切都好吗?那么我需要改变我的桌子的整个结构......但如果它很快,那么我很高兴
  • @mathew:错误 1064 是指使用了 reserved word。您使用的是什么版本的 MySQL?您是否按原样复制并粘贴了函数,并附有 DELIMITER 语句?
  • 非常感谢你给我这个测试并用很少的 SQL 知识更新我。我目前正在使用 MyISAM,所以我要创建一个新表并填充它吗?如果是这样,我该怎么做??
  • 我将创建新表,但如何用另一个表上的数据填充它
【解决方案2】:

绝对看起来像每个表中的 url 字段上的索引是要走的路

【讨论】:

    【解决方案3】:

    听起来您的WHERE 子句中的某些列可能不是indexed。索引用于快速查找具有特定列值的行。如果没有索引,MySQL 必须从第一行开始,然后读取整个表以查找相关行。

    您可能会发现EXPLAIN 有助于分析您的查询。

    【讨论】:

    • 我已经对列 'url' 上的所有表进行了索引,我已经查询了 EXPLAIN,它返回了整个表结构......但是我需要从中了解什么??
    • 哦,我已经为 url 列添加了 UNIQUE INDEX。因为我不想重复……这会造成干扰吗??
    • UNIQUE INDEX 是一个索引。至于了解EXPLAIN,请查看databasejournal.com/features/mysql/article.php/1382791/…
    • 好吧,表 4 似乎存在索引问题,它没有主键..我不知道它在创建时发生了什么..谁能告诉我如何从表中复制数据4到table4-1??
    • 糟糕,一切正常,看来我会发布结果
    【解决方案4】:

    查找 JOIN,尤其是 INNER JOINS、LEFT JOINS 和 OUTER JOINS 之间的区别。还索引您要查找的所有字段。

    【讨论】:

      【解决方案5】:

      您的索引可能有问题!

      在任何情况下,像 url 这样的长字符串会导致主键性能不佳。这在索引中占用了大量空间,因此索引没有尽可能密集,并且每个 IO 加载的行指针更少。同样使用 url,99% 的字符串都以“http://www”开头。所以数据库引擎必须比较 13 个字符才能确定一行不匹配。

      对此的一种解决方案是使用 MD5、SHA1 甚至 CRC32 之类的哈希函数从字符串中获取原始二进制值,并将该值用作表的主键。 CRC32 是一个很好的整数大小的主键,但几乎可以肯定的是,在某个阶段您会遇到两个哈希到相同 CRC32 值的 url,因此您需要存储和比较“url”字符串以确定。其他散列函数返回更长的值(在“原始”模式下分别为 16 字节和 20 字节),但发生冲突的机会非常小,不值得担心。

      .

      【讨论】:

      • @mathew 去掉 www 是错误的。例如,www.somedomain.com 与 somedomain.com 不同。是的,现在大多数网站的 DNS 条目都指向同一个 Web 服务器,但这只是一种约定。无论如何,从 URL 中删除 3 个字符不会使大多数 URL 缩短太多。
      猜你喜欢
      • 2014-12-19
      • 2010-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-07
      • 1970-01-01
      • 2022-12-16
      相关资源
      最近更新 更多