PHP实现千万级大表游标高性能分页的上一页功能方案
基于游标分页实现「上一页」的落地方案
你现在用的这套是典型的游标分页(Seek Pagination),和传统offset分页的性能差异来源就是避开了数据库逐行扫描偏移量前所有数据的开销。这套逻辑本身天然支持上一页跳转,不需要回到原来的offset方案,只要调整两点逻辑即可:
核心逻辑调整
原来的实现只在每页返回「当前页最后一条的记录ID」,用来定位下一页的查询起始点。要支持上一页,只需要在每页返回时,额外记录当前页第一条记录的ID,作为上一页查询的定位锚点,两个值分别对应相邻两个方向的查询起点,不需要额外的存储开销,也不需要服务端维护翻页状态。
具体SQL实现
假设你的表排序规则是按x字段正序排列,主键id是自增唯一值,提前给排序字段建(x, id)的联合索引保证查询走索引:
- 下一页逻辑(和你现有实现完全兼容,不需要改动):
以当前页最后一条的ID为起点,取起点之后的15条数据:select x,y,z from table_name where id > {当前页最后一条的ID} order by x asc, id asc limit 15 - 上一页逻辑:
以当前页第一条的ID为起点,先反向取起点之前紧挨着的15条记录,再把排序方向正过来,和页面展示顺序保持一致:select x,y,z from ( select x,y,z from table_name where id < {当前页第一条的ID} order by x desc, id desc limit 15 ) as prev_page_data order by x asc, id asc
性能说明
这套上一页的查询和你现在的下一页查询性能是完全一致的:
- 反向扫描
(x, id)的联合索引时,数据库只需要从起点位置反向遍历15条索引记录就可以拿到结果,不需要扫描全表,也不需要像offset一样逐行跳过之前的所有数据,耗时同样能稳定在毫秒级。 - 套子查询的写法不会带来额外的性能开销,因为子查询本身是走索引的排序扫描,外层只是对已经拿到的15条结果重新排序,数据量极小。
注意事项
- 排序规则不要只依赖
x字段:如果x列存在重复值,单纯按x排序会出现翻页时数据重复、漏行的问题,一定要把唯一主键id加到排序规则和联合索引里,保证每一行的排序顺序是固定的。 - 常见实现坑规避:千万不要直接写
where id < {当前页第一条ID} order by x asc limit15来当上一页查询,这种写法会直接拿到全表最靠前的15条数据,不是紧挨着当前页的相邻15条,翻页逻辑会完全错乱。 - 锚点值可以直接作为请求参数随页面传递,不需要存在服务端Session里,完全保留原来分页的无状态特性。
- 边界判断:如果上一页的查询返回结果为空,说明已经到全表第一页,直接把上一页按钮置灰即可。
内容的提问来源于stack exchange,提问作者M a m a D
相关产品推荐
相关产品推荐

