vlambda博客
学习文章列表

MySQL系列7 - 结合案例分析如何使用索引

案例一:有一张user表包含身份证号这一字段,且需要根据身份证号码建立索引。

解决方案1: 正常建立身份证号索引

该方案确实有效,根据身份证号可以比较快的查询出该行用户的信息。



例如我们要查询某个身份证号为 420103xxxxxxxxxxxx的用户,会从第一条以420103开头的的索引逐个匹配。


优化方案1:我们把字符串倒着存,在根据身份证号获取用户行数据时,

select * from user where card_id = reverse('420103xxxxxxxxxxxx');

虽然身份证号的反转后的字符串出现重复开头的概率也存在,但是比正向字符串出现重复的概率会小得多。


优化方案2: 我们新增一个字段就用来存储身份证号码的后六位,并作为索引,在根据身份证号获取用户行数据时,

select * from user where card_id_cc = 'xxxxxx' and card_id = '420103xxxxxxxxxxxx';


我们再来对以上优化做出总结:

1. 两种优化方式的目标都是增加索引字符串的区分度。一个通过反转实现、一个通过减少索引字符串的长度实现。

2. 优化后的方案是不支持范围查询的,这里身份证号码应该是没有范围查询的需求的。如果是其他的字段,需要考虑当前或者未来的场景是否需要范围查询,酌情使用。


我们再从业务角度出发,当前这张user表的业务场景是怎样的?如果仅仅是某个学校的学生,假设这个学校每年新增2000位学生,50年才有10万条记录,那还有必要这样优化吗?如果分别是区政府、市政府、省政府使用该系统结合着分析数据量以及身份证号前六位标识的规则,是否还有其他的优化方案?以上的优化点只是用来抛砖引玉,大家结合实际业务中灵活发挥。


案例二:仍然是案例一的表,再看下这条语句:

select * from user where card_id = 420103xxxxxxxxxxxx;

card_id明明是索引,为啥这条语句这么慢,而且explain一下,怎么全表扫描了。


分析:这是因为 card_id是 varchar(32)类型,而输入的参数却是整型,需要做类型转换。在进行类型转换时会破坏索引的有序性,因此优化器决定不走索引了。


扩展:还有一种情形也需要注意,在两张表做联合查询时,虽然看着是同一个字段,可能两张表的字符集不一样(一个是utf8mb4,另一个是utf8)。这种情况也不会命中索引。


解决方案1: 修改其中一张表的字符集

alter table user modify card_id varchar(32) CHARACTER SET utf8mb4 default null;


解决方案2: 由于表的数据量都比较大,修改字符集代价过高,直接在sql中显示调用函数

select user.* from user , table2 where user.card_id = CONVERT(table2.card_id USING utf8);


以上提到的优化点重点在于提升各位解决问题的视野,大家可以从技术的层面、业务的层面,同时还可以使用一些巧方法来优化对MySQL的使用。下一章我会结合我个人工作中遇到的实际场景来分析如何一步步做优化的。


往期回顾: