MySQL 在执行深度分页时,绝不会一次性将行数据加载到内存中

发布时间:2026/5/18 19:00:10

MySQL 在执行深度分页时,绝不会一次性将行数据加载到内存中 MySQL 在执行LIMIT 1000000, 10时绝不会一次性将前 100 万行数据加载到内存中。它的执行机制是典型的**“流式逐行处理”**内存占用是固定且可控的与offset大小无关。下面从 InnoDB 存储引擎的执行细节为你拆解真实过程 实际执行过程非内存加载而是流式扫描按页读取非按行加载InnoDB 数据存储在 16KB 的 Page 中。MySQL 会从索引/数据页的起始位置开始逐页加载到 Buffer Pool 或执行缓冲区而不是把 100 万行一次性塞进内存。逐行解析 条件判断 丢弃对页中的每一行执行引擎会解析行记录结构评估WHERE条件是否匹配参与ORDER BY排序比较如有判断当前行序号是否 offset若满足跳过条件直接丢弃不进入结果集也不长期驻留内存仅保留目标行当扫描到第1000001行时才开始将后续 10 行组装成结果集返回给客户端。内存使用上限固定整个过程使用的内存主要来自几个固定参数read_buffer_size/read_rnd_buffer_size顺序/随机读缓冲sort_buffer_size排序临时缓冲tmp_table_size/max_heap_table_size临时表这些缓冲区大小通常在几 MB 到几十 MB与 offset 无关。 为什么“不占内存”却依然极慢深度分页的性能杀手不是内存而是以下三项硬成本成本类型具体表现影响 I/O 成本即使数据在 Buffer Pool 中CPU 仍需从内存页中逐行解析若未缓存则触发大量磁盘随机/顺序读取磁盘 IOPS 飙升响应时间线性增长 CPU 成本解析 100 万行记录头、解码变长字段、执行 WHERE 过滤、排序比较消耗大量 CPU 周期查询线程长时间占用 CPU影响并发 Buffer Pool 污染大量无关数据页被加载进内存挤占热点页缓存位置导致后续查询缓存命中率下降拖慢整个实例的其他查询 排序放大效应若带ORDER BYMySQL 需对过滤后的中间结果集排序。offset 越大排序数据集越大可能触发filesort或落盘临时表内存/磁盘开销呈指数级上升✅ 核心结论LIMIT offset, size的瓶颈是“无效扫描的 I/O CPU 成本”而非“内存占用”。MySQL 采用流式处理内存使用恒定但扫描行数、回表次数、排序开销会随 offset 线性/指数增长这才是性能断崖的根源。这也正是工业界优化深度分页的底层逻辑游标分页直接定位起始点扫描行数从O(offsetsize)降至O(size)延迟关联子查询仅扫描索引不触发表数据回表大幅降低 I/O 与 CPU 解析成本覆盖索引彻底消除回表让扫描成本趋近于纯内存索引遍历如果你在实际业务中遇到具体慢查询可以提供EXPLAIN执行计划我可以帮你精准定位是回表过多、排序开销大还是 Buffer Pool 命中问题并给出针对性 SQL 改造方案。

相关新闻