【发布时间】:2014-06-23 23:52:51
【问题描述】:
我有以下 人物 表:
| Id | FirstName | Children |
|----|-----------|----------|
| 1 | mark | 4 |
| 2 | paul | 0 |
| 3 | mike | 3 |
请注意,我在 FirstName 中有一个非唯一索引,在 Children 中有另一个索引。
我需要获取每个有孩子的人的前 10000 个名字和孩子数量。所以我决定采用这个解决方案:
SELECT firstName, children FROM people
WHERE children > 0
ORDER BY children DESC
LIMIT 0, 10000
问题是从包含 260 万条记录的表中返回结果需要 4 秒。这是解释:
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
|----|-------------|--------|-------|---------------|----------|---------|--------|------------|-------------|
| 1 | SIMPLE | people | range | children | children | 4 | (null) | 2677610 | Using where |
正如我所见,范围告诉我正在扫描索引并与一个值进行比较(在本例中为 children > 0)。我会说这应该足够快。然后,我的猜测是,在获取所有匹配的索引元素后,DBMS 通过将索引中的值与表中的值进行内部连接,从表中获取 firstName。
如果我将上一段翻译成 SQL,我会得到如下内容:
SELECT firstName, children FROM people
JOIN (
SELECT id FROM people
WHERE children > 0
ORDER BY children DESC
LIMIT 0, 10000
) s
ON people.id = s.id
ORDER BY children DESC
上一条SQL语句的解释是:
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
|----|-------------|------------|--------|---------------|----------|---------|--------|---------|---------------------------------|
| 1 | PRIMARY | <derived2> | ALL | (null) | (null) | (null) | (null) | 10000 | Using temporary; Using filesort |
| 1 | PRIMARY | p | eq_ref | PRIMARY | PRIMARY | 4 | s.id | 1 | |
| 2 | DERIVED | people | range | children | children | 4 | (null) | 2687462 | Using where; Using index |
令我惊讶的是,这个查询的执行速度比第一个查询快几倍。但是,我增加 LIMIT X 的次数越多,这种差异就越大(例如:对于 LIMIT 1000000, 10000,第二个查询仍然不到 1 秒,第一个查询超过 20秒)。这导致我提出以下问题:
- MySQL 处理第一个查询与第二个查询有何不同?
- 有没有办法提示 MySQL 以执行第二个查询的方式执行第一个查询?
- 可以公平地说,从中吸取的教训是,每当我想获取不属于所使用索引的值时,双排序和连接是正确的方法吗?
补充说明:
- SQLFiddle(如果有什么不同的话)
- 请注意,我正在使用 SQL_NO_CACHE 运行查询
- MySQL 版本:5.5.37
【问题讨论】:
-
第一次查询使用索引提示
SELECT * FROM table1 USE INDEX (col1_index) WHERE col1=1;dev.mysql.com/doc/refman/5.0/en/index-hints.html -
实际上,我已经尝试过类似的方法,方法是强制它用于 JOIN 的主键和 ORDER BY 的子索引。但我也没有运气。我也试过你提到的那个,我也没有运气。
-
尝试使用Analyze Table 更新您的统计信息并再次运行第一个查询
-
阅读这个人的博客mysqlperformanceblog.com/2006/09/01/…他提出了一些提高性能的方法
标签: mysql sql performance indexing