mysql如何高效分页

本文介绍了一种使用MySQL进行高效分页的方法,通过利用ID作为线索进行前后页跳转,大幅减少了每次查询需要扫描的数据量,有效提升了高并发场景下的查询性能。

转自:http://www.dev26.com/bbs/topic/389

最近一直在考虑网站改造的问题,因为我实在受不了来回的折腾hibernate、JPA这些东西了,有学这些的时间我还不如用这时间研究一下SQL,当然也是我个人比较懒,现在就把我收集的关于mysql高交往分页的一些知识点列出来。

先看一下分页的基本原理(我拿的是优快云那个百万级数据库来测试!):

1
SELECT  FROM  `csdn`  ORDER  BY  id  DESC  LIMIT 100000,2000;


耗时: 0.813ms

分析:对上面的mysql语句说明:limit 100000,2000的意思扫描满足条件的102000行,扔掉前面的100000行,返回最后的2000行。

问题就在这里,如果是limit 100000,20000,需要扫描120000行,在一个高并发的应用里,每次查询需要扫描超过100000行,性能肯定大打折扣。

在《efficient pagination using mysql》中提出的clue方式。

利用clue方法,给翻页提供一些线索,比如还是SELECT * FROM `csdn` order by id desc,按id降序分页,每页2000条,当前是第50页,当前页条目id最大的是102000,最小的是100000。如果我们只提供上一页、下一页这样的跳转(不提供到第N页的跳转)。

那么在处理上一页的时候SQL语句可以是:

1
SELECT  FROM  `csdn`  WHERE  id<=102000  ORDER  BY  id  DESC  LIMIT 2000; #上一页


耗时:0.015ms

处理下一页的时候SQL语句可以是:

1
SELECT  FROM  `csdn`  WHERE  id>102000  ORDER  BY  id  ASC  LIMIT 2000; #下一页


耗时:0.015ms

这样,不管翻多少页,每次查询只扫描20行。效率大大提高了!

但是,这样分页的缺点是只能提供上一页、下一页的链接形式。

 

<think>好的,用户想了解MySQL高效分页的实现方法,特别是优化OFFSET的技巧。首先,我需要回忆一下传统的分页方法。通常人们会用LIMIT和OFFSET,比如SELECT * FROM table LIMIT 10 OFFSET 20;。不过当数据量大的时候,OFFSET会导致性能问题,因为它需要扫描并跳过前面的所有行,这会很慢。 然后,用户提到优化技巧,可能想知道如何避免使用OFFSET。我记得有一种方法是用WHERE子句配合自增主键来代替OFFSET。例如,记录上一页最后一条的ID,然后查询ID大于该值的行,这样可以利用索引,减少扫描的数据量。比如SELECT * FROM table WHERE id > 20 LIMIT 10;。这种方法叫做“游标分页”或者“seek method”,性能更好,但只适用于有序且连续的查询,不能跳页。 还有覆盖索引(Covering Index)的优化,如果查询只需要索引中的字段,就可以直接从索引中获取数据,避免回表操作。比如SELECT id FROM table ORDER BY id LIMIT 10 OFFSET 20;,这时候如果id有索引,性能会提升。 延迟关联(Deferred Join)也是一种方法,先通过子查询获取主键,再关联原表,这样减少需要处理的数据量。例如SELECT * FROM table INNER JOIN (SELECT id FROM table ORDER BY id LIMIT 10 OFFSET 20) AS tmp USING(id);。 另外,可能还需要考虑其他因素,比如使用书签(Bookmark)保存上次查询的位置,或者预计算总页数。但预计算总页数在数据频繁变化时可能不准确。 还要注意索引的使用,确保ORDER BY和WHERE中的列有合适的索引,否则即使不用OFFSET,性能也可能不佳。比如按时间排序的分页,应该在时间字段上建立索引。 可能需要提醒用户,虽然这些方法能优化性能,但各有适用场景。比如游标分页适合无限滚动,而无法跳转到任意页。延迟关联在需要多列数据时可能更有效。 最后,要确认这些方法是否有实际案例或者引用,比如Percona或MySQL官方文档有没有相关建议。可能需要检查是否有遗漏的优化点,比如分区表或者使用内存表等高级技术,但通常分页优化还是集中在避免全表扫描和利用索引上。</think>### MySQL高效分页实现方法 #### 传统分页的性能瓶颈 传统分页使用$LIMIT/OFFSET$语法: $$SELECT * FROM table ORDER BY id LIMIT 10 OFFSET 20;$$ 当OFFSET值较大时,MySQL需要扫描并丢弃前N行数据,时间复杂度为$O(N)$,在百万级数据时可能产生秒级延迟[^1]。 #### 核心优化方案 1. **游标分页(Keyset Pagination)** ```sql SELECT * FROM table WHERE id > 20 -- 记录上次查询的最后ID值 ORDER BY id LIMIT 10; ``` - 利用聚簇索引特性,时间复杂度优化至$O(1)$ - 限制:仅支持顺序翻页,无法直接跳转任意页码 2. **覆盖索引优化** ```sql SELECT * FROM table INNER JOIN ( SELECT id FROM table ORDER BY create_time LIMIT 10 OFFSET 20 ) AS tmp USING(id); ``` - 子查询通过覆盖索引快速定位 - 主查询通过主键回表获取完整数据 3. **延迟关联(Deferred Join)** ```sql SELECT * FROM table WHERE create_time <= '2023-01-01' ORDER BY create_time DESC LIMIT 10 OFFSET 20; ``` - 对排序字段建立组合索引:$INDEX(create_time, id)$ - 扫描索引树即可完成排序,避免全表扫描 #### 进阶优化技巧 - **预计算分页边界**:缓存每页的起始ID - **分布式ID设计**:使用单调递增的Snowflake ID - **分区表优化**:对时间序列数据按范围分区 - **内存分页缓存**:使用Redis缓存热点查询结果 #### 性能对比 | 方法 | 10万数据(ms) | 100万数据(ms) | |--------------------|-------------|--------------| | 传统LIMIT/OFFSET | 120 | 1200 | | 游标分页 | 2 | 3 | | 延迟关联 | 15 | 150 | [^1]: Percona性能测试表明,当OFFSET超过总数据量的10%时性能急剧下降
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值