在业务系统中,当数据量较大时,一次性返回所有数据会给数据库和前端都带来较大压力,因此需要通过分页的方式分批获取数据。MySQL中实现分页的核心关键字是LIMIT,它可以直接控制查询结果返回的记录条数和起始位置。

一、LIMIT关键字基本语法
LIMIT的常用语法有两种形式,第一种是只指定返回的记录数:
-- 只返回前10条记录,常用于获取最新数据、热门数据等场景 SELECT * FROM user_table ORDER BY create_time DESC LIMIT 10;
第二种是指定起始偏移量和返回记录数,语法为LIMIT offset, row_count,其中offset表示跳过前面多少条记录,row_count表示返回多少条记录:
-- 跳过前20条记录,返回接下来的10条记录,对应第3页数据(每页10条) SELECT * FROM user_table ORDER BY id ASC LIMIT 20, 10;
二、常规分页查询实现
2.1 单表分页
假设我们有一个用户表user_info,包含id、username、age、create_time字段,需要按照创建时间倒序分页展示用户列表,每页显示10条数据,那么第n页的查询语句可以写成:
-- 第n页的查询,offset计算方式为 (n-1)*10 SELECT id, username, age, create_time FROM user_info ORDER BY create_time DESC LIMIT (n-1)*10, 10;
这里需要注意,LIMIT后面的offset必须是非负整数常量,不能直接使用表达式,因此在实际开发中,offset的值需要先由程序计算好再传入SQL中。
2.2 联表分页
如果是多表关联查询的分页,比如需要查询用户及其对应的订单数量,分页返回用户信息,那么需要先确保关联查询的顺序稳定,再进行分页:
-- 用户表和订单表关联,查询每个用户的订单数,分页返回 SELECT u.id, u.username, COUNT(o.id) AS order_count FROM user_info u LEFT JOIN order_info o ON u.id = o.user_id GROUP BY u.id, u.username ORDER BY u.id ASC LIMIT 20, 10;
三、深度分页的性能问题
当offset的值很大时,比如LIMIT 100000, 10,MySQL需要先扫描前100000条记录,然后丢弃这些记录,再返回后面的10条,这个过程的效率会非常低,尤其是在大表上执行时,查询耗时可能会急剧上升。
我们可以通过EXPLAIN分析这类语句的执行计划,会发现即使有索引,扫描的行数也会包含offset的所有行,造成不必要的性能损耗。
四、分页优化方案
4.1 基于自增主键的游标分页
如果表的主键是自增的,且分页是按照主键顺序查询,可以使用主键作为游标,避免大offset的问题:
-- 上一页最后一条记录的id是last_id,下一页查询id大于last_id的10条记录 SELECT id, username, age, create_time FROM user_info WHERE id > 100000 ORDER BY id ASC LIMIT 10;
这种方式的优势是只需要扫描10条记录,不需要跳过前面的数据,性能稳定,适合无限滚动加载的场景,但要求排序字段是连续递增且唯一的。
4.2 延迟关联优化
如果分页必须基于其他字段排序,比如创建时间,可以使用延迟关联的方式,先通过索引获取主键,再回表查询完整数据:
-- 先通过索引查询出需要的主键,再关联原表获取完整数据
SELECT u.id, u.username, u.age, u.create_time
FROM user_info u
INNER JOIN (
SELECT id
FROM user_info
ORDER BY create_time DESC
LIMIT 100000, 10
) tmp ON u.id = tmp.id;
这种方式可以减少回表的次数,因为子查询只需要扫描索引获取主键,不需要读取完整行数据,性能会比直接查询好很多。
4.3 业务层限制最大分页深度
在实际业务中,很少有用户会翻到很深的页码,因此可以限制最大查询的页码,比如最多只允许查询前100页,超过则返回空数据,从业务层面避免深度分页的问题。
五、分页注意事项
- 分页查询时一定要加上
ORDER BY子句,否则返回的记录顺序是不确定的,分页结果会出现混乱。 - 如果使用
ORDER BY的字段没有唯一性,可能会导致不同页出现重复记录,建议排序字段加上唯一主键作为二级排序条件。 - 联表分页时,要确保排序字段有对应的索引,否则查询性能会很差。
通过以上方法,开发者可以根据不同的业务场景选择合适的MySQL分页实现方案,在保证功能正确的同时,也能获得较好的查询性能。