1 | CREATE TABLE `tradelog` ( |
条件字段函数操作
select count(*) from tradelog where month(t_modified)=7;
B+树的快速定位能力,来源于同一层兄弟节点的有序性。
对索引字段做函数操作,可能会破坏索引值的有序性,优化器决定放弃走树搜索功能(快速定位能力)。但优化器并不是要放弃使用这个索引。
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | tradelog | NULL | index | NULL | t_modified | 6 | NULL | 100335 | 100.00 | Using where;Using index |
放弃了树搜索功能,优化器可以选择遍历主键索引,也可以选择遍历索引t_modified,优化器对比索引大小后发现,索引t_moditied更小,遍历这个索引比遍历主键索引更快,最终会选择索引t_modified。
key=t_modified表示使用了该索引;测试数据10W行,rows=100335说明扫描了整个索引的所有值;Extra的Using index,表示使用了覆盖索引。
由于在t_modified字段加了month()函数操作,MySQL无法再使用索引快速定位功能,只能使用全索引扫描。
即使是对于不改变有序性的函数,也不会考虑使用索引。(where id + 1 = 10000;—>where id = 10000 - 1;)
隐式类型转换
select * from tradelog where tradeid=110717;
tradeid字段类型是varchar(32),输入参数是整型,需要做类型转换。
在MySQL中,字符串和数字做比较的话,是将字符串转换成数字。(索引字段为整型,条件字段为字符串时,走索引。截断式转换)
select * from tradelog where CAST(tradid AS signed int) = 110717;
触发对索引字段做函数操作,优化器会放弃走树搜索功能。
隐式字符编码转换
1 | mysql> CREATE TABLE `trade_detail` ( |
select d.* from tradelog l, trade_detail d where d.tradeid=l.tradeid and l.id=2;
- 优化器会先在交易记录表tradelog上查到id=2的行,这个步骤用上了主键索引,只扫描一行。
- 没有用上交易详情表trade_detail上的tradeid索引,进行了全表扫描。通过遍历主键索引的方式,一个一个的判断tradeid的值是否匹配。
这个执行计划里,从tradelog表中取tradeid字段,再去trade_detail表里查询匹配字段。把tradelog称为驱动表,trade_detail称为被驱动表,把tradeid称为关联字段。
这两个表的字符集不同,一个是utf8,一个是utf8mb4,做表连接查询的时候用不上关联字段的索引。字符集utf8mb4是utf8的超集,当这两个类型的字符串做比较的时候,MySQL内部的操作是,先把utf8字符串转成utf8mb4字符集,再做比较。
select * from trade_detail where CONVERT(traideid USING utf8mb4)=$L2.tradeid.value;
– convert()函数,把输入的字符串转成utf8mb4字符集。
触发对索引字段做函数操作,优化器会放弃走树搜索功能。
连接过程中要求在被驱动表的索引字段上加函数操作,是直接导致对被驱动表做全表扫描的原因。
select l.operator from tradelog l , trade_detail d where d.tradeid=l.tradeid and d.id=4;
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | d | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
1 | SIMPLE | l | NULL | ref | tradeid | tradeid | 131 | const | 1 | 100.00 | NULL |
驱动表trade_detail,被驱动表tradelog。
select opterator from tradelog where tradeid = CONVERT($R4.tradeid.value USING utf8mb4);
CONVERT函数是加在输入参数上的,可以用上被驱动表的tradeid索引。
原SQL改造:
- 把trade_detail表上的tradeid字段的字符集改成utf8mb4,避免字符集转换问题。
alter table trade_detail modify tradeid varchar(32) CHARACTER set utf8mb4 default null;
- 修改SQL语句。
select d.* from tradelog l , trade_detail d where d.tradeid=CONVERT(l.tradeid USING utf8) and l.id=2;