核心内容摘要
亭亭玉立,月下丁香:一段穿越时空的东方雅韵
实现上一页和下一页不是简单地用LIMIT offset, size而是通过游标分页Cursor-based Pagination实现高性能、可扩展的分页。
核心原理为什么不用 OFFSET▶
OFFSET 的致命缺陷-- 跳过 100 万行 → 扫描 1,000,010 行SELECT*FROMordersLIMIT1000000,10;问题性能随页码线性下降深度分页直接拖垮数据库▶
游标分页的优势-- 仅扫描 10 行SELECT*FROMordersWHEREid1000000ORDERBYidLIMIT10;优势性能恒定O(
适合无限滚动/深度分页核心认知“上一页/下一页” 记录当前页首尾 ID而非计算偏移量
完整实现PHP MySQL▶
数据库准备-- 创建测试表CREATETABLEproducts(idINTAUTO_INCREMENTPRIMARYKEY,nameVARCHAR(
NOTNULL,priceDECIMAL(10,
NOTNULL);-- 插入测试数据INSERTINTOproducts(name,price)VALUES(Product 1,
10.
,(Product 2,
20.
,...(Product 100,
1000.
;▶
PHP 分页逻辑?php// config.php$pdonewPDO(mysql:hostlocalhost;dbnametest,user,pass);$pdo-setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);// pagination.phpfunctiongetProducts($pdo,$directionnext,$cursornull,$limit
{if($directionnext){// 下一页id cursor$sqlSELECT * FROM products WHERE id :cursor ORDER BY id ASC LIMIT :limit;$params[cursor$cursor??0,limit$limit];}else{// 上一页id cursor按降序取再反转$sqlSELECT * FROM products WHERE id :cursor ORDER BY id DESC LIMIT :limit;$params[cursor$cursor,limit$limit];}$stmt$pdo-prepare($sql);$stmt-bindValue(:cursor,$params[cursor],PDO::PARAM_INT);$stmt-bindValue(:limit,$params[limit],PDO::PARAM_INT);$stmt-execute();$rows$stmt-fetchAll(PDO::FETCH_ASSOC);// 上一页结果需反转if($directionprev){$rowsarray_reverse($rows);}return$rows;}// 获取当前页数据$direction$_GET[direction]??next;$cursor$_GET[cursor]??null;$productsgetProducts($pdo,$direction,$cursor);// 提取首尾 ID$firstId$products?$products[0][id]:null;$lastId$products?end($products)[id]:null;?▶
前端 HTML!-- index.php --!DOCTYPEhtmlhtmlheadtitle产品列表/title/headbodyh1产品列表/h1ul?php foreach ($products as $product): ?li? htmlspecialchars($product[name]) ?- $? $product[price] ?/li?php endforeach; ?/ul!-- 分页按钮 --div?php if ($firstId): ?ahref?directionprevcursor? $firstId ?上一页/a?php endif; ??php if ($lastId): ?ahref?directionnextcursor? $lastId ?下一页/a?php endif; ?/div/body/html
关键机制解析▶
下一页逻辑参数cursor 当前页最后一条的 id查询WHERE id cursor ORDER BY id ASC结果直接获取下一页 10 条▶
上一页逻辑参数cursor 当前页第一条的 id查询WHERE id cursor ORDER BY id DESC倒序取处理PHP 中array_reverse()还原顺序▶
边界处理首页cursor null→WHERE id 0末页无数据 → 隐藏“下一页”按钮
避坑指南陷阱破局方案忽略 XSS 风险输出时用htmlspecialchars()未处理空结果检查$products是否为空并发插入导致漏数据接受最终一致性业务可容忍非自增主键确保排序字段是聚簇索引
终极心法**“分页不是跳转而是锚点的传递——当你记录首尾 ID你在跳过扫描当你反转上一页你在还原顺序当你隐藏无效按钮你在优化体验。
真正的分页始于对索引的敬畏成于对细节的精控。
”结语从今天起永远用游标分页替代 OFFSET上一页用array_reverse()处理输出数据必用htmlspecialchars()因为最好的分页不是计算偏移而是精准传递每一程的锚点。