MySQL 索引 优化 Using filesort

MySQL 数据库,MyISAM 类型的表 table_item,有 5、6 个字段,主键是 id。

user_id 和 item_id 两个字段都是单独的 INDEX 类型的索引。

问题是如何发现的?

今天搞程序,在自己的开发环境下,打开一个简单的页面都要好长时间,不知道问题出在哪里,只发现硬盘灯闪个不停。

观察 Windows 的任务管理器,看到 mysqld-nt.exe 这个进程的 “I/O 读取字节” 高达十几 G!!!

再次测试,发现每次刷新页面,这个进程要读取几十 M 的数据。

奇了怪了,查看 SQL 语句,还有表结构,字段都建了索引了呀。

后来 EXPLAIN 了一下,看到结果是 Using where; Using filesort。

explain SELECT * FROM table_item WHERE user_id = 2 ORDER BY item_id LIMIT 0, 5

翻了 MySQL 手册,仔细看下 filesort 的说明,知道了 Using filesort 是一种速度很慢的外部排序。

不过我不理解为什么会使用 filesort 排序,WHERE 和 ORDER BY 用到的字段都是有索引的呀。

赶紧 Google,找到几篇解释 Using filesort 的文章,得到的启示就是索引定义不当,MySQL 没有用到索引。

记得以前 Chenbin 给俺们培训过 MySQL 的优化,我还参加了两次,总感觉自己了解了如何优化 MySQL,没想到这么简单的语句我都没能优化。

现在想起来一些 MySQL 的特性了。

  1. 一条 SQL 语句只能使用 1 个索引 (5.0-),MySQL 根据表的状态,选择一个它认为最好的索引用于优化查询
  2. 联合索引,只能按从左到右的顺序依次使用

这 2 点刚好可以解决我的问题。

user_id 和 item_id 是 2 个索引,我的语句中,MySQL 选择了 user_id,那么 item_id 的索引没有起到任何用处,所以,当我要排序的时候,由于记录数较多,内存中的排序 buffer 满了,只能 Using filesort 进行外部排序,因此每次查询要从磁盘读取几十 M 的数据,太慢了。

修改表结构,删除 user_id 和 item_id 的 INDEX 索引,建立一个名为 user_item 的联合 UNIQUE 索引,顺序是先 user_id 后 item_id,再 EXPLAIN,这回只有 Using where 了。

再刷新页面,观察任务管理器,mysqld-nt.exe 只读取了 2K 的数据,页面咔的一下就出来了……

Tags:

4 comments

  1. 关于索引:
    一条sql语句里面关联的表每个表只能用一个索引,5.0之后如果查询条件里面有多个索引字段,mysql会尝试进行 index merge,同时使用几个索引的合集。

  2. explain select * from `gf_share` WHERE hide=1 order by c_time desc
    楼主 我在使用这样一条sql语句的时候为什么还说我Using filesort

  3. 我觉得原因有两点:
    (1) hide 值只有 0 和 1 ,这种字段设置索引没有意义,一个数值出现次数超过 20%,MySQL 就会放弃使用这样的索引。(20% 是我估计的,经验值)
    (2)c_time 字段没有索引,或者是没使用这个索引。另外,如果符合条件的记录数太多,即便是有索引,也是 filesort。如果记录数比较少,比如几十条,那么即使是 filesort 也没关系。据说,filesort 是一个算法的名字,不是说一定要存到磁盘上某个文件里,再排序。在内存里也可能是 filesort。所以,关键点是符合条件的记录数是不是太多。

Leave a Reply

Your email address will not be published.

*