mysql优化实战:千万级数据表如何进行分页查询?

上次在MysqL8上导入一个1000万数据之后,今天就想验证看看MysqL对于单表大数据的分页的表现情况,并探讨一下单表大数据分页的优化思路。

如何导入千万数据请参考我的上篇文章MysqL千万级数据量插入的几种方案耗时,看完就知道如何选择

测试环境

简单说明下测试环境。

MysqL版本:8.0.17在test测试数据库中新增一个表t_user,字段为ID和NAME。其中在ID字段设置索引,且name值不同。导入1千万条数据,之前有部分测试数据,最后数据量为10000000,见如下图:

传统的分页查询

一般开发人员对于web分页解决方案是使用limit子句来完成,语法为:

limit n,m

对于小规模数据应用来讲,可能并不会有大问题,在一定程度上还是比较有效率的,但是如果在大数据应用来讲,它的效率就可能很低了,因为它是全表扫描,数据越往后,即ID越大,数据越慢,例如下面的例子

select * from t_user LIMIT 90000 ,100 ##耗时 1秒

select * from t_user LIMIT 900000 ,100 ##耗时 2秒

select * from t_user LIMIT 9000000 ,100 ##耗时 6秒

耗时依次增加

因为我们字段比较少,所以可能执行起来没想象中的慢,我们可以用执行计划来验证一下

explain select * from t_user LIMIT 1000000 ,100

从结果看出执行全表扫描,所以如果只取前面几条,则还是比较容易,如果是取相对靠后的数据,则全表扫描对数据库压力时非常大,特别web应用并发查询,则很容易造成数据库响应慢。

优化思路1

很明显,既然做全表扫描引起效率低下,那么我们第一印象肯定想办法是不是不走全表扫描。可以这么做,先在索引ID上完成分页操作,然后再根据ID关联回去。查询代码更新如下:

1.索引ID完成分页

select id from t_user order by id limit 9000000,100

2.最后完成自关联,最终sql如下:

select * from t_user a inner join (select id from t_user order by id limit 9000000,100) b on a.id = b.id

3.看看执行计划,可见已经没有再全表扫描,这种方式是减少扫描来提高分页效率。

最终执行时间为3秒,效率提高一倍。

优化思路2

使用子查询进行优化,原理是通过子句先定位查询起始ID值,然后再通过主查询语句的ID往后查询,此方法适用ID排序的情况。举例如下:

select * from t_user where

id>=(select id from t_user limit 9000000,1)

limit 100;

通过执行计划查看,它也属于非全表扫描,所以效率同第一种优化方案

优化思路3

把LIMIT 查询转换成某个位置的查询,此思路需要和应用开发进行结合,不是单纯的数据库优化,因为我们实现的是web的分页查询,所以是可以通过页面的现实记录数和当前页,计算出该页的ID范围,我们拼出以下sql

select * from t_user where id between 9000000 and 9000100 limit 100;

耗时只需0.4豪秒,是非常快速查询,也是我们在生产中最常见的一种实现方法

总结

优化方案2和优化3其实都是把LIMIT m,n转换成 limit n的查询,适合排序字段唯一的情况,当然大部分场景我们的排序字段都会选主键ID,这样操作的确能减轻分页的压力。理论上,几千万数据在MysqL8上还是可以支撑的。

如果在数据量更大的情况下,这几种方案都无法满足了,一般我们得在数据库层和应用层之间设置缓存数据库,再执行分页查询

如果本文对你有用,请加关注,后续会有更多的运维开发经验。

相关文章

MySQL 死锁 是指两个或多个事务互相等待对方持有的锁,从而导...
在MySQL中,InnoDB引擎通过Next-Key Locking技术来解决幻读问...
在数据库事务管理中,Undo Log 和 Redo Log 是两种关键日志,...
case when概述 sql语句中的case语句与高级语言中的switch语句...
其实很简单,只是为了忘记,做个记录,用的时候方便。 不管是...
1.进入服务,找到mysql服务,在属性里找到mysql的安装路径 2...