函数会导致SQL性能下降?

这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战

平时我们写SQL的时候,是不是过于潇洒,如果不好好检查,若是出现了性能问题,可能会给数据库造成压力,今天,我们就和林晓斌老师,一起来看看吧~话说,这种情况,我今天也写在公司的代码里了,我寻思明天要不要把它改了哈哈~

条件字段函数操作

假设你现在维护了一个交易系统,其中交易记录表 tradelog 包含交易流水号 (tradeid)、交易员 id(operator)、交易时间(t_modified)等字段。这个表的建表语句如下:

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;
复制代码

假设,现在已经记录了从2016年初到2018年底的所有数据,我们要统计这三年七月的交易记录总数,我们会怎么写呢?

select count(*) from tradelog where month(t_modified)=7;

ps:其实我还不认识这个函数呢 解释:`MONTH`函数返回一个[整数],表示指定日期值的月份。
复制代码

我们执行后会发现,执行了很久才返回数据?如果对字段做了函数计算,就用不上索引了,这是MySQL的规定。

为什么条件是 where t_modified='2018-7-1’的时候可以用上索引,而改成 where month(t_modified)=7 的时候就不行了?

image.png

我们来看看上面的图,因为我们给t_modified建立了索引,所以MySQL会建立一棵t_modified索引树,如果我们的SQL执行的是where t_modified='2018-7-1’,引擎就会按照上面 绿色箭头的路线,快速定位到t_modified='2018-7-1’需要的结果。引擎能这么快找到,借助的正是B+树同一层兄弟节点的有序性。(想了解B树和B+树的不同的,可以参考下这篇文章我们为什么选择B+树来做索引?

所以,经过函数month()计算后的结果 7 ,在树的第一层就已经不知道该怎么办了。所以,对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。

但是,优化器并不是放弃使用这个索引,虽然优化器放弃了树搜素功能,但是还能遍历主键索引和索引 t_modified,优化器经过对比后,会发现t_modified比逐渐索引更小,所以最终还是会选择索引 t_modified

image.png

Extra 字段的 Using index,表示的是使用了覆盖索引。 也就是说,由于在 t_modified 字段加了 month() 函数操作,导致了全索引扫描。为了能 够用上索引的快速定位能力,我们就要把 SQL 语句改成基于字段本身的范围查询。

select count(*) from tradelog where
(t_modified >= '2016-7-1' and t_modified<'2016-8-1') or
(t_modified >= '2017-7-1' and t_modified<'2017-8-1') or
(t_modified >= '2018-7-1' and t_modified<'2018-8-1')
复制代码

是因为我们改变了有序性,所以才失效的吗?也不全是:

select * from tradelog where id + 1 = 10000 
复制代码

这个 SQL 语句, 这个加 1 操作并不会改变有序性,但是 MySQL 优化器还是不能用 id 索引快速定位到 9999 这一行。所以,需要你在写 SQL 语句的时候,手动改写成 where id = 10000 -1 才可以。

猜你喜欢

转载自juejin.im/post/7036389347175694373