0%

相同逻辑,不同SQL,性能差异

1
2
3
4
5
6
7
8
9
CREATE TABLE `tradelog` (
`id` int(11) NOT NULL,
`tradeid` varchar(32) DEFAULT NULL,
`operator` int(11) DEFAULT NULL,
`t_modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `tradeid` (`tradeid`),
KEY `t_modified` (`t_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

条件字段函数操作

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
2
3
4
5
6
7
8
mysql> CREATE TABLE `trade_detail` (
`id` int(11) NOT NULL,
`tradeid` varchar(32) DEFAULT NULL,
`trade_step` int(11) DEFAULT NULL, /*操作步骤*/
`step_info` varchar(32) DEFAULT NULL, /*步骤信息*/
PRIMARY KEY (`id`),
KEY `tradeid` (`tradeid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

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字符集。
触发对索引字段做函数操作,优化器会放弃走树搜索功能。

连接过程中要求在被驱动表的索引字段上加函数操作,是直接导致对被驱动表做全表扫描的原因。

原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;