黑客入门技能
2026/6/22 13:53:41
SELECT * FROM table LIMIT 1000000, 10是典型的深度分页查询,表面看是“跳过 100 万行取 10 行”,实则触发全表扫描 + 内存排序,导致磁盘 I/O 爆炸、响应时间飙升。
LIMIT offset, size?offset + size行:💡核心认知:
LIMIT offset, size的成本 = O(offset + size),而非 O(size)
filesort(磁盘临时文件)-- 第一页SELECT*FROMordersWHEREid>0ORDERBYidLIMIT10;-- 第二页(假设上一页最大 id=100)SELECT*FROMordersWHEREid>100ORDERBYidLIMIT10;range→ 直接定位起始点SELECTt.*FROMorders tINNERJOIN(SELECTidFROMordersORDERBYidLIMIT1000000,10)tmpONt.id=tmp.id;// 缓存第 100000 页起始 ID$startId=Redis::get('page_100000_start_id');$rows=DB::select("SELECT * FROM orders WHERE id >= ? ORDER BY id LIMIT 10",[$startId]);if($page>100){thrownewException('超过最大页数');}| 陷阱 | 破局方案 |
|---|---|
盲目使用OFFSET | 深度分页必用游标方案 |
| 忽略排序字段选择 | 游标字段必须是索引且唯一(如自增 ID) |
| 宽表全字段查询 | 仅SELECT必要字段,减少回表 |
**“LIMIT 不是分页,
而是性能的悬崖——
- 当你使用 OFFSET,
你在支付线性成本;- 当你切换游标,
你在享受常数时间;- 当你限制深度,
你在守护系统。真正的查询优化,
始于对执行计划的敬畏,
成于对细节的精控。”
从今天起:
WHERE id > last_id)EXPLAIN验证执行计划(避免Using filesort)因为最好的分页,
不是跳过百万行,
而是精准定位下一程。