SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 10;高性能分页查询的典范,它通过 游标分页(Cursor-based Pagination) 避免了传统 OFFSET 的性能陷阱。


一、执行机制:为什么高效?

▶ 1. 执行计划解析
EXPLAIN SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 10;
  • 关键输出
    type: range
    key: PRIMARY
    rows: 10
    Extra: Using where; Using index
    
▶ 2. 执行流程

定位 id=1000000

通过聚簇索引向右扫描

读取下 10 行

返回结果

  • 核心优势
    • 仅扫描 10 行(而非 OFFSET 的 1,000,010 行)
    • 直接利用聚簇索引(InnoDB 主键即数据存储顺序)

💡 核心认知
WHERE id > N + ORDER BY id = 直接跳转到 N+1 位置


二、索引利用:聚簇索引的威力

▶ 1. InnoDB 聚簇索引结构
  • 数据存储
    • 主键索引的叶子节点 = 完整行数据
    • 物理存储按主键顺序排列
  • 查询优势
    • WHERE id > N → 直接定位到 B+ 树的 N+1 位置
    • 顺序读取后续 10 行 → 无随机 I/O
▶ 2. 对比非聚簇索引
  • MyISAM 表
    • 主键索引 ≠ 数据存储顺序
    • 需额外回表 → 性能下降
  • InnoDB 非主键查询
    • id 非主键 → 需回表 → 性能下降

📌 关键点
此查询高效的前提是 id 为 InnoDB 聚簇索引(通常是自增主键)


三、工程价值:游标分页的实践

▶ 1. 前端交互设计
  • 传递游标
    • 前端保存上一页最后一条的 id
    • 下一页请求携带 cursor=id
  • 示例 API
    GET /orders?cursor=1000000&limit=10
    
▶ 2. 后端实现
// Laravel 示例
public function index(Request $request)
{
    $cursor = $request->input('cursor', 0);
    $limit = min($request->input('limit', 10), 100);
    
    $orders = Order::where('id', '>', $cursor)
                   ->orderBy('id')
                   ->limit($limit)
                   ->get();
    
    return response()->json([
        'data' => $orders,
        'next_cursor' => $orders->last()?->id
    ]);
}
▶ 3. 性能对比
查询方式 扫描行数 响应时间(1亿行表)
LIMIT 1000000, 10 1,000,010 12.3 秒
游标分页 10 0.008 秒

四、避坑指南

陷阱 破局方案
id 非自增主键 确保排序字段是聚簇索引
并发插入导致漏数据 接受最终一致性(业务可容忍)
反向分页困难 单独实现 WHERE id < cursor ORDER BY id DESC

五、终极心法

**“游标不是技巧,
而是索引的舞蹈——

  • 当你 利用聚簇
    你在消除随机;
  • 当你 传递锚点
    你在跳过扫描;
  • 当你 接受最终一致
    你在拥抱现实。

真正的分页优化,
始于对存储的敬畏,
成于对细节的精控。”


结语

从今天起:

  1. 深度分页必用游标方案
  2. 确保排序字段是聚簇索引
  3. EXPLAIN 验证执行计划(type=range)

因为最好的分页,
不是跳过百万行,
而是精准定位下一程。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐