获取最后一条记录的性能之争

    对于没有dba的情况下,sql的性能就成了后端开发要考虑的问题之一了,不然就会影响接口的响应性能。

    本次接口需求实际就是获取设备的最后一条上报数据,但是这个设备上报数据量那可是海量。所以后面的查找肯定时间会变长。先看我写的2条sql吧。

一、场景回顾

第一条sql的逻辑:

因为记录的主键是自增的,所以最后一条的id肯定最大,所以先根据设备编码过滤上报数据,然后使用max函数取最大id,然后再次查询使用主键过滤。

SELECT
    e.* 
FROM
    device_electricity_record e 
WHERE
    e.id = ( SELECT MAX( r.id ) FROM device_electricity_record r WHERE r.device_code = '004A7701240078D7' );
第二条sql的逻辑:

根据设备编码过滤数据,然后根据上报时间降序排序,最后取最上面的一条 。     
SELECT
    e.* 
FROM
    device_electricity_record e 
WHERE
    e.device_code = '004A7701240078D7' 
ORDER BY
    e.record_time DESC 
    LIMIT 1;

以上2条sql达到的效果是一样的,现在就是考虑性能问题了,后期上报量肯定是很多的,我就觉得这个排序会影响性能。

二、查询计划分析

初步体验:

获取最后一条记录的性能之争

获取最后一条记录的性能之争

看到没,基本差异不大,目前这个表的数据量是1.5w多。

执行计划查看

device_code字段不建索引:

获取最后一条记录的性能之争

获取最后一条记录的性能之争

使用EXPLAIN查看执行计划。

select_type:表示SELECT的类型,常见的取值有:

类型 说明
SIMPLE 简单表,不使用表连接或子查询
PRIMARY 主查询,即外层的查询
UNION UNION中的第二个或者后面的查询语句
SUBQUERY 子查询中的第一个

type:表示MySQL在表中找到所需行的方式,或者叫访问类型。常见访问类型如下,从上到下,性能由差到最好:

ALL 全表扫描
index 索引全扫描
range 索引范围扫描
ref 非唯一索引扫描
eq_ref 唯一索引扫描
const,system 单表最多有一个匹配行
NULL 不用扫描表或索引

 

possible_keys: 表示查询可能使用的索引

key: 实际使用的索引

key_len: 使用索引字段的长度

ref: 使用哪个列或常数与key一起从表中选择行。

rows: 扫描行的数量

filtered: 存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例(百分比)

Extra: 执行情况的说明和描述,包含不适合在其他列中显示但是对执行计划非常重要的额外信息

最主要的有一下三种:

Using Index 表示索引覆盖,不会回表查询
Using Where 表示进行了回表查询
Using Index Condition 表示进行了ICP优化
Using Flesort 表示MySQL需额外排序操作, 不能通过索引顺序达到排序效果

 

了解上面基本信息后可以看到,2条sql都是全表扫描,数据扫描量都是整个表的记录数量。第一条sql有2次计划,第二条sql有额外的排序操作。这么看,其实我没看出那条更优秀。唯一就在想额外排序操作数据量大是肯定耗时间的,那MAX函数就不排序吗?我觉得MAX底层应该不是排序取的最大,而是比较替换,就是说每一条记录出来我都比较上一次记录,记录较大的值(这里有时间验证下我的理解,估计需要看mysql的源码)。

device_code增加索引:

ALTER TABLE `mp20190122`.`device_electricity_record` 
ADD INDEX `deviceCodeIndex`(`device_code`) USING BTREE COMMENT '设备编码增加索引';

获取最后一条记录的性能之争

获取最后一条记录的性能之争

补充Extra描述:

select tables optimized away

在没有GROUP BY子句的情况下,基于索引优化MIN/MAX操作,或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。

看到这,你是不是又想到一条取最新数据的sql,使用MAX函数根据设备编码分组,然后再连接使用DEVICE_CODE=查询。

SELECT
    d.* 
FROM
    ( SELECT MAX( r.id ) AS id FROM device_electricity_record r GROUP BY r.device_code ) rr,
    device_electricity_record d 
WHERE
    d.id = rr.id 
    AND d.DEVICE_CODE = '004A7701240078D7';

获取最后一条记录的性能之争

它的计划有全表扫描,比加索引效果差,还麻烦。直接可以干掉了。

到此,其实我觉得应该还是使用第一条性能最强悍。等数据量上来了,后面再试试吧。有NB的大神看到了,麻烦回复我下指正。为大家的分享精神点赞哦。