MySQL:为什么用了索引还是'using filesort'?
MySQL: why still 'using filesort' when indexes are used?
mysql-5.6.24-win32.1432006610
我有两个用户消息表。
TMessageBody (id, body)
存储消息正文。
TMessage (id, uid, folderId, msgBodyId, subject)
将用户的消息存储在收件箱、发件箱等文件夹中。
创建TableSQL:
create table TMessageBody (
id int unsigned not null primary key auto_increment,
body text not null
);
create table TMessage (
id int unsigned not null primary key auto_increment,
uid int unsigned not null,
folderId int unsigned not null,
msgBodyId int unsigned not null,
subject varchar(256) not null
);
部分测试数据:
insert into TMessageBody
(body) values
('Here is body 1')
,('Here is body 2')
,('Here is body 3')
,('Here is body 4')
,('Here is body 5')
,('Here is body 6')
,('Here is body 7')
,('Here is body 8')
,('Here is body 9')
;
insert into TMessage
(uid, folderId, msgBodyId, subject) values
(1, 999, 1, 'Hello jack')
, (1, 999, 2, 'Jack, how are you')
, (1, 888, 3, 'Good morning jack')
, (2, 888, 4, 'I love you, rose')
, (2, 999, 5, 'I love you, rose')
, (3, 888, 6, 'Peter, please call back')
, (3, 999, 7, 'What are you doing, Peter')
, (3, 999, 8, 'Happy birthday, perter')
, (4, 999, 9, 'Let me know if you are ready')
;
索引:
create index Idx_MsgBodyId on TMessage(msgBodyId);
create index Idx_Uid_FolderId on TMessage(uid, folderId);
1.FileSort 显示 folderId
不在 WHERE 子句中
以下查询获取给定用户 ID 的所有消息,包括消息正文:
SET @uid=3;
SET @folderId=999;
EXPLAIN
SELECT *
FROM TMessage
INNER JOIN TMessageBody
ON TMessage.msgBodyId=TMessageBody.id
WHERE TMessage.uid=@uid
#AND TMessage.folderId=@folderId
ORDER BY TMessage.id DESC
;
解释结果是:
mysql> EXPLAIN
-> SELECT *
-> FROM TMessage
-> INNER JOIN TMessageBody
-> ON TMessage.msgBodyId=TMessageBody.id
-> WHERE TMessage.uid=@uid
-> #AND TMessage.folderId=@folderId
-> ORDER BY TMessage.id DESC
-> ;
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
| 1 | SIMPLE | TMessage | ref | Idx_MsgBodyId,Idx_Uid_FolderId | Idx_Uid_FolderId | 4 | const | 3 | Using where; Using filesort |
| 1 | SIMPLE | TMessageBody | eq_ref | PRIMARY | PRIMARY | 4 | test.TMessage.msgBodyId | 1 | NULL |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
2 rows in set (0.00 sec)
2.FileSort 当 folderId
在 WHERE 子句中时消失
除了WHERE子句外,查询同上:
SET @uid=3;
SET @folderId=999;
EXPLAIN
SELECT *
FROM TMessage
INNER JOIN TMessageBody
ON TMessage.msgBodyId=TMessageBody.id
WHERE TMessage.uid=@uid
AND TMessage.folderId=@folderId
ORDER BY TMessage.id DESC
;
解释结果是:
mysql> EXPLAIN
-> SELECT *
-> FROM TMessage
-> INNER JOIN TMessageBody
-> ON TMessage.msgBodyId=TMessageBody.id
-> WHERE TMessage.uid=@uid
-> AND TMessage.folderId=@folderId
-> ORDER BY TMessage.id DESC
-> ;
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-------------+
| 1 | SIMPLE | TMessage | ref | Idx_MsgBodyId,Idx_Uid_FolderId | Idx_Uid_FolderId | 8 | const,const | 2 | Using where |
| 1 | SIMPLE | TMessageBody | eq_ref | PRIMARY | PRIMARY | 4 | test.TMessage.msgBodyId | 1 | NULL |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-------------+
2 rows in set (0.00 sec)
问题:
两个查询的区别在于folderId
列是否在WHERE
子句中。根据 EXPLAIN 结果,两个查询都使用 Idx_Uid_FolderId
索引。我想知道为什么一个显示 FileSort 而另一个不显示。
更新
试图在第一个查询中使用 ORDER BY TMessage.folderId, TMessage.id DESC
。但是 Using filesort
仍然存在于 EXPLAIN 结果中。
mysql> EXPLAIN
-> SELECT *
-> FROM TMessage
-> INNER JOIN TMessageBody
-> ON TMessage.msgBodyId=TMessageBody.id
-> WHERE TMessage.uid=@uid
-> #AND TMessage.folderId=@folderId
-> ORDER BY TMessage.folderId, TMessage.id DESC
-> ;
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
| 1 | SIMPLE | TMessage | ref | Idx_MsgBodyId,Idx_Uid_FolderId | Idx_Uid_FolderId | 4 | const | 3 | Using where; Using filesort |
| 1 | SIMPLE | TMessageBody | eq_ref | PRIMARY | PRIMARY | 4 | test.TMessage.msgBodyId | 1 | NULL |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
2 rows in set (0.06 sec)
将索引视为连接值。
在这种情况下,您的索引是
uid | folderId | id
id 排在最后,因为它在您的二级索引中引用了您的主键。
在第一种情况下,您按 uid 过滤,然后按 id 排序。问题是 MySQL 不能假定 id 已按时排序,因为索引实际上是在按 id 过滤之前按 folderId 排序的。
例如:
uid | folderId | id
1 | 1 | 1
1 | 2 | 2
2 | 1 | 3
因此该索引不能用于排序,因为索引的排序不同于 order by 子句。
现在,我的问题是:您为什么要避免文件排序?除非您遇到性能问题,否则使用文件排序非常好。尽管有名称,排序是在内存中完成的,除非另有说明(使用临时)。
mysql-5.6.24-win32.1432006610
我有两个用户消息表。
TMessageBody (id, body)
存储消息正文。
TMessage (id, uid, folderId, msgBodyId, subject)
将用户的消息存储在收件箱、发件箱等文件夹中。
创建TableSQL:
create table TMessageBody (
id int unsigned not null primary key auto_increment,
body text not null
);
create table TMessage (
id int unsigned not null primary key auto_increment,
uid int unsigned not null,
folderId int unsigned not null,
msgBodyId int unsigned not null,
subject varchar(256) not null
);
部分测试数据:
insert into TMessageBody
(body) values
('Here is body 1')
,('Here is body 2')
,('Here is body 3')
,('Here is body 4')
,('Here is body 5')
,('Here is body 6')
,('Here is body 7')
,('Here is body 8')
,('Here is body 9')
;
insert into TMessage
(uid, folderId, msgBodyId, subject) values
(1, 999, 1, 'Hello jack')
, (1, 999, 2, 'Jack, how are you')
, (1, 888, 3, 'Good morning jack')
, (2, 888, 4, 'I love you, rose')
, (2, 999, 5, 'I love you, rose')
, (3, 888, 6, 'Peter, please call back')
, (3, 999, 7, 'What are you doing, Peter')
, (3, 999, 8, 'Happy birthday, perter')
, (4, 999, 9, 'Let me know if you are ready')
;
索引:
create index Idx_MsgBodyId on TMessage(msgBodyId);
create index Idx_Uid_FolderId on TMessage(uid, folderId);
1.FileSort 显示 folderId
不在 WHERE 子句中
以下查询获取给定用户 ID 的所有消息,包括消息正文:
SET @uid=3;
SET @folderId=999;
EXPLAIN
SELECT *
FROM TMessage
INNER JOIN TMessageBody
ON TMessage.msgBodyId=TMessageBody.id
WHERE TMessage.uid=@uid
#AND TMessage.folderId=@folderId
ORDER BY TMessage.id DESC
;
解释结果是:
mysql> EXPLAIN
-> SELECT *
-> FROM TMessage
-> INNER JOIN TMessageBody
-> ON TMessage.msgBodyId=TMessageBody.id
-> WHERE TMessage.uid=@uid
-> #AND TMessage.folderId=@folderId
-> ORDER BY TMessage.id DESC
-> ;
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
| 1 | SIMPLE | TMessage | ref | Idx_MsgBodyId,Idx_Uid_FolderId | Idx_Uid_FolderId | 4 | const | 3 | Using where; Using filesort |
| 1 | SIMPLE | TMessageBody | eq_ref | PRIMARY | PRIMARY | 4 | test.TMessage.msgBodyId | 1 | NULL |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
2 rows in set (0.00 sec)
2.FileSort 当 folderId
在 WHERE 子句中时消失
除了WHERE子句外,查询同上:
SET @uid=3;
SET @folderId=999;
EXPLAIN
SELECT *
FROM TMessage
INNER JOIN TMessageBody
ON TMessage.msgBodyId=TMessageBody.id
WHERE TMessage.uid=@uid
AND TMessage.folderId=@folderId
ORDER BY TMessage.id DESC
;
解释结果是:
mysql> EXPLAIN
-> SELECT *
-> FROM TMessage
-> INNER JOIN TMessageBody
-> ON TMessage.msgBodyId=TMessageBody.id
-> WHERE TMessage.uid=@uid
-> AND TMessage.folderId=@folderId
-> ORDER BY TMessage.id DESC
-> ;
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-------------+
| 1 | SIMPLE | TMessage | ref | Idx_MsgBodyId,Idx_Uid_FolderId | Idx_Uid_FolderId | 8 | const,const | 2 | Using where |
| 1 | SIMPLE | TMessageBody | eq_ref | PRIMARY | PRIMARY | 4 | test.TMessage.msgBodyId | 1 | NULL |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-------------+
2 rows in set (0.00 sec)
问题:
两个查询的区别在于folderId
列是否在WHERE
子句中。根据 EXPLAIN 结果,两个查询都使用 Idx_Uid_FolderId
索引。我想知道为什么一个显示 FileSort 而另一个不显示。
更新
试图在第一个查询中使用 ORDER BY TMessage.folderId, TMessage.id DESC
。但是 Using filesort
仍然存在于 EXPLAIN 结果中。
mysql> EXPLAIN
-> SELECT *
-> FROM TMessage
-> INNER JOIN TMessageBody
-> ON TMessage.msgBodyId=TMessageBody.id
-> WHERE TMessage.uid=@uid
-> #AND TMessage.folderId=@folderId
-> ORDER BY TMessage.folderId, TMessage.id DESC
-> ;
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
| 1 | SIMPLE | TMessage | ref | Idx_MsgBodyId,Idx_Uid_FolderId | Idx_Uid_FolderId | 4 | const | 3 | Using where; Using filesort |
| 1 | SIMPLE | TMessageBody | eq_ref | PRIMARY | PRIMARY | 4 | test.TMessage.msgBodyId | 1 | NULL |
+----+-------------+--------------+--------+--------------------------------+------------------+---------+-------------------------+------+-----------------------------+
2 rows in set (0.06 sec)
将索引视为连接值。
在这种情况下,您的索引是
uid | folderId | id
id 排在最后,因为它在您的二级索引中引用了您的主键。
在第一种情况下,您按 uid 过滤,然后按 id 排序。问题是 MySQL 不能假定 id 已按时排序,因为索引实际上是在按 id 过滤之前按 folderId 排序的。
例如:
uid | folderId | id
1 | 1 | 1
1 | 2 | 2
2 | 1 | 3
因此该索引不能用于排序,因为索引的排序不同于 order by 子句。
现在,我的问题是:您为什么要避免文件排序?除非您遇到性能问题,否则使用文件排序非常好。尽管有名称,排序是在内存中完成的,除非另有说明(使用临时)。