MySQL里分页其实就是用LIMIT来搞定,怎么写比较方便点呢?
- 问答
- 2026-01-11 14:19:41
- 2
MySQL里分页其实就是用LIMIT来搞定,怎么写比较方便点呢?这个问题的答案其实挺简单的,但里面也有一些小技巧可以让它用起来更顺手,尤其是在数据量变大之后,咱们就从一个最简单的样子开始,慢慢聊到怎么让它变得“方便”。
最基础的写法:LIMIT offset, count
根据W3Schools和MySQL官方手册这类最基础的教程,LIMIT子句最直接的用法就是告诉数据库两件事:从第几行开始拿(offset),以及一共要拿多少行(count),它的标准写法是 LIMIT [偏移量], [要返回的记录数]。
举个例子,你的网站有一个文章列表,一页想显示10篇文章。
- 第一页的SQL就是这样写:
SELECT * FROM articles ORDER BY create_time DESC LIMIT 0, 10;这里的意思是,按照创建时间倒序排好(新的在前),从第0行开始(也就是最开始),取10条记录。
- 那第二页呢?
SELECT * FROM articles ORDER BY create_time DESC LIMIT 10, 10;从第10行开始(也就是跳过头10条),再取10条。
- 第三页:
LIMIT 20, 10,以此类推。
这种方式非常直观,一看就懂,你只需要在后台代码里,根据用户当前是第几页(比如叫 page_num),计算出偏移量 offset = (page_num - 1) * page_size,然后把 offset 和 page_size 拼接到SQL里就行了,这是几乎所有刚开始用MySQL分页的人都会用的方法。

为什么说“可能”不方便?
上面这个方法在小数据量的时候,又快又简单,没什么问题,就像很多技术博客(比如几年前很多程序员喜欢看的CSDN博客、博客园里的相关文章)里提到的那样,当你的数据量达到几十万、几百万甚至更多的时候,这个 LIMIT offset, count 的写法就会暴露出一个性能问题。
问题就出在那个 offset(偏移量)上,数据库执行 LIMIT 100000, 10 的时候,它并不是直接神奇地跳到第100000条记录然后给你10条,它需要先从头开始扫描,数过前面的100000条记录,然后把它们都“扔掉”,最后才取接下来的10条,你可以想象,当offset的值非常大时,这个“数数”和“扔掉”的过程会非常耗时,就像让你在一本很厚的书中,从第1000页开始读,你得先一页一页地翻过前面999页一样。
当你发现你的网站越到后面分页加载越慢,甚至超时,很可能就是这个原因。
怎么写能方便点、快一点?

既然知道了瓶颈在于大的偏移量,那优化的核心思路就是想办法避免使用大的offset,这里有几个比较实用的方法。
-
使用WHERE子句配合自增ID(最常用的优化手段) 这是最有效也最被推荐的优化方法,在很多关于数据库性能优化的讨论中都会被提及,它的前提是你的表有一个自增的主键ID(
idINT AUTO_INCREMENT PRIMARY KEY)。思路是:不让数据库去计算偏移量,而是记住上一页最后一条记录的ID,还拿文章列表的例子说:
- 第一页不变:
SELECT * FROM articles ORDER BY id DESC LIMIT 10;(假设按ID倒序) - 当你要取第二页时,你已经拿到了第一页最后一条记录的ID,假设是
95,那么第二页的SQL就可以写成:SELECT * FROM articles WHERE id < 95 ORDER BY id DESC LIMIT 10; - 这句话的意思是:找出所有ID比95小的记录,按ID倒序排,取前10条,这就完美地跳过了前10条,直接定位到了第11条开始的位置,数据库可以利用主键索引快速定位到ID<95的地方,完全避免了全表扫描和前10万条数据的临时丢弃,速度极快。
这种方法非常方便,尤其是在“上一页”、“下一页”这种场景下,但它有个小限制,就是不太方便直接跳转到任意页(比如从第1页直接跳到第100页),因为你需要知道第99页最后一条记录的ID是多少,在移动端App的瀑布流或者无限滚动加载中,这几乎是标准做法。
- 第一页不变:
-
使用子查询优化 如果不能用上面的ID方法,或者排序条件不是主键,还可以尝试用子查询,这个思路在一些数据库专家的分享中能看到。

原查询是:
SELECT * FROM articles ORDER BY create_time DESC LIMIT 100000, 10;可以改写成:
SELECT * FROM articles INNER JOIN ( SELECT id FROM articles ORDER BY create_time DESC LIMIT 100000, 10 ) AS tmp USING(id);这个写法的巧妙之处在于,它先在一个子查询里,利用可能更小的索引(如果create_time有索引)快速地只取出对应偏移量的那些记录的主键ID,因为只取ID,数据量小,即使有offset,也比取全部字段快,再用这些确定的主键ID,通过JOIN操作去获取这些记录的完整信息,这相当于把一次大的偏移操作,拆成了两次效率更高的查询。
-
预先计算或缓存 对于一些不经常变化的分页数据,比如商品分类、城市列表等,最简单的“方便”之道就是不要每次都去查数据库,你可以在第一次查询后,把结果整个或者分页地缓存在Redis或Memcached这样的缓存里,用户再次请求相同页码时,直接从缓存中读取,速度是最快的。
总结一下
回到最初的问题“MySQL里分页其实就是用LIMIT来搞定,怎么写比较方便点呢?”。
- 数据量小,怎么方便怎么来:直接用
LIMIT offset, page_size,在代码里动态计算offset,这是最省事的。 - 数据量大,追求性能:首推 “WHERE id > ?” 配合自增ID 的方法,这是从算法层面解决问题的根本办法,尤其适合顺序翻页的场景。
- 数据量大,且无法用ID优化:可以考虑 子查询 的方式,虽然SQL复杂了一点,但通常能带来显著的性能提升。
- 数据不常变:缓存是最大的“方便”,一劳永逸。
说白了,方便与否取决于你的具体场景,了解每种方法的原理和适用条件,就能在面对不同情况时,选出最“方便”的那一种写法。
本文由称怜于2026-01-11发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/78732.html