我有一个超过900万行的超大表,在我的软件中,我需要在循环中快速对其进行选择查询。问题是查询需要将近4秒钟才能完成。这是其中一个查询(它们都是相似的,因为它们都有相同的WHERE子句,这会减慢它们的速度:
SELECT MIN(id)
FROM `04c1Tg0M`
WHERE `tried` = 0;
我正在使用已尝试的列作为布尔值。值为1或0.以下是该查询的EXPLAIN:
--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+------+---------+------+---------+--------------------------+
| 1 | SIMPLE | 04c1Tg0M | index | NULL | pdex | 158 | NULL | 9275107 | Using where; Using index |
+----+-------------+----------+-------+---------------+------+---------+------+---------+--------------------------+
这是表结构:
CREATE TABLE `04c1Tg0M` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`tried` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `pdex` (`username`,`id`,`tried`)
) ENGINE=MyISAM AUTO_INCREMENT=9275108 DEFAULT CHARSET=utf8
以下是show indes的输出:
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| 04c1Tg0M | 0 | PRIMARY | 1 | id | A | 9275107 | NULL | NULL | | BTREE | |
| 04c1Tg0M | 1 | pdex | 1 | username | A | 9275107 | NULL | NULL | | BTREE | |
| 04c1Tg0M | 1 | pdex | 2 | id | A | 9275107 | NULL | NULL | | BTREE | |
| 04c1Tg0M | 1 | pdex | 3 | tried | A | 9275107 | NULL | NULL | YES | BTREE | |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
以下是相关查询的输出:
+---------+
| MIN(id) |
+---------+
| 1 |
+---------+
1 row in set (3.76 sec)
我需要大幅缩短查询时间。非常感谢任何帮助。
答案 0 :(得分:6)
您应该在(tried, id)
上添加索引。
您已在(username,id,tried)
添加了索引,但此索引无法有效用于您编写的查询,因为字段tried
不是索引中的第一项。
MySQL使用索引进行这些操作:
查找特定索引列
MIN()
的{{1}}或MAX()
值。这是由预处理器优化的,该预处理器检查您是否在索引中key_col
之前的所有关键部分上使用WHERE key_part_N = constant
。在这种情况下,MySQL对每个key_col
或MIN()
表达式执行单个键查找,并将其替换为常量。如果所有表达式都替换为常量,则查询立即返回。例如:SELECT MIN(key_part2),MAX(key_part2) FROM tbl_name WHERE key_part1 = 10;
请特别注意,MAX()
子句必须引用之前出现的列要为其计算WHERE
或MIN
的列,这就是为什么你当前的指数没有得到有效利用的原因。
我需要在循环中快速对其进行选择查询
您确定需要循环查询吗?您是否考虑过将循环移动到数据库中?也许你真正需要的是一个加入?发送一个获取大量数据的查询比使用每个只获取少量数据的大量小查询更好。
答案 1 :(得分:2)
在tried
列上添加索引可以加快查询速度。
MySQL使用索引进行这些操作:
查找特定索引列key_col的MIN()或MAX()值。这是由预处理器优化的,该预处理器检查您是否在索引中的key_col之前出现的所有关键部分上使用WHERE key_part_N =常量。在这种情况下,MySQL对每个MIN()或MAX()表达式执行单个键查找,并用常量替换它。如果所有表达式都替换为常量,则查询立即返回。例如:
SELECT MIN(key_part2),MAX(key_part2)FROM tbl_name WHERE key_part1 = 10;
答案 2 :(得分:0)
如果您在tried
列上添加索引,您的查询应该加快。
您可以使用
之类的命令create index tried_ix1 on 04c1Tg0M (tried);
作为旁注,如果您在InnoDb表上应用索引,请同时考虑是否应该对该索引进行聚类。例如,如果您打算以特定的,可能是排序的顺序访问许多表记录,也许是报表,那么请考虑根据该报表的排序要求创建聚簇索引。