Mysql-深度分页

深度分页是指在数据库中,通过偏移量(OFFSET)跳过大量记录后,再获取少量数据。当数据量非常庞大时,深度分页会严重影响查询性能,甚至导致数据库崩溃。

深度分页优化是一个复杂的问题,需要综合考虑数据库设计、应用架构、硬件配置等多个因素。通过合理的索引设计、分页策略、缓存、分表分库等手段,可以有效提升系统性能,满足用户需求。

那如果头铁就是要对单表进行深度分页,有什么好的优化策略吗?实际上有的,这就是本文要说的了。

深度分页为什么慢

首先我们来分析下为什么深度分页性能低下。假设订单表有主键 id 和 creat_time 两个索引。需要根据创建时间来分页。

执行如下的分页查询:

select * FROM t_order ORDER BY create_time desc LIMIT 1000000,100;

mysql 会在 create_time 索引树上查询 1000000 + 100 条数据,然后再回表查询 1000000 + 100 条数据,再丢弃前面的 1000000 条数据,最后返回 100 条数据。这个过程中会产生大量的回表和临时数据,mysql 优化器会判断索引失效,进行全表查询,所以很慢。

知道了原因,那么优化重点是尽可能减少回表数据量。

解决方案

使用子查询

SELECT * FROM t_order 
WHERE create_time <= (
SELECT create_time FROM t_order ORDER BY create_time desc LIMIT 1000000,1
)
ORDER BY create_time desc LIMIT 100;

子查询获取到第 1000000+1 条 create_time ,子查询只返回 create_time ,所以在二级索引上不需要回表。把他作为条件传给主查询,主查询只需要取 100 条数据即可,回表也只是回 100 条 。

使用连接查询

SELECT t_order.* FROM t_order 
inner join (
select id from t_order ORDER BY create_time desc LIMIT 1000000,100 
) tmp ON tmp.id = t_order.id 
ORDER BY create_time desc ;

在 create_time 索引上查询出主键 id 的临时表,次临时表不需要回表,再把临时表和表本身通过主键进行关联,回表次数只有 100 次,且用了主键关联,更快了。

连续分页

-- 我们模拟连续分页到第1000000页,最小的一条数据的create_time
SELECT * FROM t_order  
ORDER BY create_time ASC  LIMIT 1000000, 1

SELECT * FROM t_order  
WHERE create_time <= '2023-01-22 00:00:00' 
ORDER BY create_time desc LIMIT 100;

每次查询保留查询的结果,下次查询时用上一次查询的结果进行过滤。这样就不需要子查询和关联查询了,效率最高。但是这种情况不支持随机跨页查询,只能连续分页。

总结

虽然我们可以通过一些手段来降低深度分页的耗时,但这也仅仅是权宜之计,在真实开发中,我们应该避免深度分页的业务场景,或者采用分库分表,ES存储等策略。

SystemCaller
SystemCaller

https://gravatar.com/noisily745e35dad0

文章: 47

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注