Skip to content

P71 MyBatis 查 100 万页还在用 Limit Offset?回去等通知吧 ​

面试题:数据量很大(百万级)时,分页还在用 LIMIT offset, size,有什么问题?怎么优化?

1. 问题 ​

sql
SELECT * FROM t ORDER BY id LIMIT 1000000, 20;

MySQL 会扫描并丢弃前 100 万行,offset 越大越慢(详见 P32 深分页)。在 MyBatis 里用 PageHelper 传 pageNum 也是同样原理——翻页越深,SQL 越慢。

2. MyBatis 场景下的优化 ​

① 游标式分页(推荐) ​

不用 pageNum,用上一页的最后一条记录:

xml
<select id="pageByCursor" resultType="Order">
    SELECT * FROM t_order
    WHERE id &gt; #{lastId}
    ORDER BY id
    LIMIT #{size}
</select>
java
List<Order> page = orderMapper.pageByCursor(lastId, 20);

每次只扫 20 行,翻 100 万页也不慢。

② 延迟关联 ​

sql
SELECT o.* FROM t_order o
JOIN (SELECT id FROM t_order ORDER BY id LIMIT 1000000, 20) tmp
ON o.id = tmp.id;

子查询只走主键覆盖索引,再回表取 20 行。

③ 覆盖索引 ​

列表页只查必要字段,建立覆盖索引((status, create_time, id)),避免回表。

④ 限制深度 ​

  • 业务上禁止跳页翻到很深的页码,用"加载更多";
  • PageHelper 可设置 reasonable 参数防越界,但治标不治本。

3. PageHelper 的注意点 ​

  • PageHelper 的 count 查询在大表上也很慢(可以关掉 count);
  • 分页 SQL 会包一层 count + limit,offset 深一样慢;
  • 动态 SQL/多表查询时 PageHelper 可能拦截错 SQL。

4. 加分点 ​

  • 说明"深分页慢的本质是 OFFSET 扫描丢弃";
  • 提到配合 ES 等搜索引擎做海量数据搜索分页(from+size 也有深度限制,用 search_after);
  • 结合业务:管理后台的"翻到第 10 万页"没有意义,产品上就该限制。

一句话总结 ​

深分页 LIMIT offset,size 会扫描并丢弃海量行;MyBatis 场景用游标式(WHERE id > lastId)+ 延迟关联/覆盖索引替代 PageHelper 的深翻页,并配合业务限制翻页深度。

基于 VitePress 重建