MySQL 如何正确的使用索引( 六 )


数字字段和字符串比较什么效果呢?如下:
mysql> select * from test1 where id = '4000000';+---------+-----------------+-----+-------------------------+| id | name | sex | email |+---------+-----------------+-----+-------------------------+| 4000000 | javacode4000000 | 2 | javacode4000000@163.com |+---------+-----------------+-----+-------------------------+1 row in set (0.00 sec)mysql> select * from test1 where id = 4000000;+---------+-----------------+-----+-------------------------+| id | name | sex | email |+---------+-----------------+-----+-------------------------+| 4000000 | javacode4000000 | 2 | javacode4000000@163.com |+---------+-----------------+-----+-------------------------+1 row in set (0.00 sec)

id上面有主键索引,id是int类型的,可以看到,上面两个查询都非常快,都可以正常利用索引快速检索,所以如果字段是数组类型的,查询的值是字符串还是数组都会走索引 。
函数使索引无效
mysql> select a.name+1 from test1 a where a.name = 'javacode1';+----------+| a.name+1 |+----------+| 1 |+----------+1 row in set, 1 warning (0.00 sec)mysql> select * from test1 a where concat(a.name,'1') = 'javacode11';+----+-----------+-----+-------------------+| id | name | sex | email |+----+-----------+-----+-------------------+| 1 | javacode1 | 1 | javacode1@163.com |+----+-----------+-----+-------------------+1 row in set (2.88 sec)
name上有索引,上面查询,第一个走索引,第二个不走索引,第二个使用了函数之后,name所在的索引树是无法快速定位需要查找的数据所在的页的,只能将所有页的记录加载到内存中,然后对每条数据使用函数进行计算之后再进行条件判断,此时索引无效了,变成了全表数据扫描 。
结论:索引字段使用函数查询使索引无效 。
运算符使索引无效
mysql> select * from test1 a where id = 2 - 1;+----+-----------+-----+-------------------+| id | name | sex | email |+----+-----------+-----+-------------------+| 1 | javacode1 | 1 | javacode1@163.com |+----+-----------+-----+-------------------+1 row in set (0.00 sec)mysql> select * from test1 a where id+1 = 2;+----+-----------+-----+-------------------+| id | name | sex | email |+----+-----------+-----+-------------------+| 1 | javacode1 | 1 | javacode1@163.com |+----+-----------+-----+-------------------+1 row in set (2.41 sec)
id上有主键索引,上面查询,第一个走索引,第二个不走索引,第二个使用运算符,id所在的索引树是无法快速定位需要查找的数据所在的页的,只能将所有页的记录加载到内存中,然后对每条数据的id进行计算之后再判断是否等于1,此时索引无效了,变成了全表数据扫描 。
结论:索引字段使用了函数将使索引无效 。
使用索引优化排序
我们有个订单表t_order(id,user_id,addtime,price),经常会查询某个用户的订单,并且按照addtime升序排序,应该怎么创建索引呢?我们来分析一下 。
在user_id上创建索引,我们分析一下这种情况,数据检索的过程:
  1. 走user_id索引,找到记录的的id
  2. 通过id在主键索引中回表检索出整条数据
  3. 重复上面的操作,获取所有目标记录
  4. 在内存中对目标记录按照addtime进行排序
我们要知道当数据量非常大的时候,排序还是比较慢的,可能会用到磁盘中的文件,有没有一种方式,查询出来的数据刚好是排好序的 。
我们再回顾一下mysql中b+树数据的结构,记录是按照索引的值排序组成的链表,如果将user_id和addtime放在一起组成联合索引(user_id,addtime),这样通过user_id检索出来的数据自然就是按照addtime排好序的,这样直接少了一步排序操作,效率更好,如果需addtime降序,只需要将结果翻转一下就可以了 。
总结一下使用索引的一些建议
  1. 在区分度高的字段上面建立索引可以有效的使用索引,区分度太低,无法有效的利用索引,可能需要扫描所有数据页,此时和不使用索引差不多
  2. 联合索引注意最左匹配原则:必须按照从左到右的顺序匹配,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整
  3. 查询记录的时候,少使用*,尽量去利用索引覆盖,可以减少回表操作,提升效率
  4. 有些查询可以采用联合索引,进而使用到索引下推(IPC),也可以减少回表操作,提升效率
  5. 禁止对索引字段使用函数、运算符操作,会使索引失效


    推荐阅读