场景

该场景来源自他人分享。 一张邀请绑定表,一个用户绑定了几十万个记录。 索引列为uid-用户的id,create_time-关系的创建时间。 查询需求比较简单: 根据创建时间倒序,取一页的记录

select * from bind where uid = xxx order by create_time desc limit 10;

分析

  1. 第一次查询比较慢,后续查询会变快,原因可能是第一次需要将几十万的数据页加载到Buffer pool,后续可以直接从内存中读取。
  2. 通过explain发现索引命中了创建时间,而非预期的uid,如果该用户上次绑定是在上个月,那他需要扫描这个所有的新增绑定,才能找到他的那10条数据。这样非常不稳定。
  3. 如果设置强制走uid索引(force index(id_uid)),explain中extra会显示出现了using file sort,这里是优化器不选择走uid索引的原因。因为即使只查询10条数据,但是需要把几十万条数据查出来以后进行排序。 如何影响优化优化器选择哪条索引呢? 主要有三种方式:
    • force index(index_name),强制优化器只使用指定的索引,如果该索引无法用于检索数据,MySQL将返回错误。
    select * from table_name force index(index_name) where condition;
    
    • use index(index_name),告诉查询优化器仅使用列出的索引来查找数据,如果没有列出索引,查询优化器将根据表中的数据选择一个。
    select * from table_name using index(index_name) where condition;
    
    • ignore index(index_name),告诉优化器忽略列出的索引。查询优化器会考虑表中的其他索引,但不会考虑被忽略的那些。
    select * from table_name ignore index(index_name) where condition;
    

其中force index和using index有什么区别吗? force index列出的索引无法用来查询数据,则报错。 using index列出的索引无法用来查询数据时,则忽略这个提示。

  1. 可以通过将create_time和uid建立联合索引的方式来做,但是生产环境数据比较大,增加这样的索引会增加不少的负担,并且如果想要最新的数据,除了时间,使用id倒序也可以,最后uid,id天然的形成了倒序的效果。