limit仅作用于当前select语句末尾,不阻止全表扫描;大offset分页性能差,应优先用游标分页(如where id>12345 order by id limit 20),并确保排序字段有索引。

MySQL 用 LIMIT 控制返回行数,不是“限制查询条数”的通用开关
很多人以为 LIMIT 是像 MAX_ROWS 那样全局约束查询结果的“保险丝”,其实不是。LIMIT 只作用于当前 SELECT 语句,且必须显式写在 SQL 末尾。它不阻止全表扫描,也不降低索引使用成本——只是把结果截掉一部分。
- 没加
LIMIT的查询,哪怕只想要第 1 条,MySQL 仍可能扫描全部匹配行(尤其没合适索引时) -
LIMIT 10和LIMIT 10000,10性能差异极大:后者要先跳过前 1 万行,如果跳过的数据没走索引覆盖,就是纯磁盘扫 - 某些 ORM(如 Django 的
QuerySet)默认不加LIMIT,即使你只取.first(),也可能生成无LIMIT的 SQL,得看具体驱动行为
分页用 LIMIT offset, size,但 offset 大了就慢
传统分页写法是 LIMIT 2000, 20,意思是跳过前 2000 行,取 20 行。问题在于:MySQL 必须定位到第 2001 行的物理位置,过程中所有被跳过的行仍要读入内存、做条件过滤(除非索引完全覆盖)。
- 当
offset > 10000,响应时间常呈线性增长,不是翻倍,是“越来越卡” -
ORDER BY id+LIMIT不能替代游标分页;id 有空缺或并发插入时,会漏数据或重复 - 如果排序字段不是主键或唯一索引,
LIMIT结果可能不稳定(相同值的行顺序不保证)
替代方案:用游标(cursor-based)分页避免大 offset
本质是把“我要第 N 页”改成“我要比上次最后一条更新的数据”。不依赖行号,而依赖有序字段的值。
- 上一页最后一条是
id = 12345,下一页就查:SELECT * FROM t WHERE id > 12345 ORDER BY id LIMIT 20 - 必须确保
WHERE字段和ORDER BY字段一致,且该字段有索引(最好是主键或唯一索引) - 不能用
OFFSET实现“跳转到第 100 页”,游标只支持逐页向前;需要随机跳转时,仍得用LIMIT,但应限制最大可跳页数(比如只允许前 1000 页) - 注意 NULL 值:如果排序字段允许 NULL,
WHERE id > ?会跳过所有 NULL,需额外处理
LIMIT 在非 SELECT 语句中基本无效
有人试过在 UPDATE 或 DELETE 里加 LIMIT 想“只改前 10 条”,这确实可行,但行为和 SELECT 完全不同:
-
UPDATE t SET x=1 WHERE y=2 LIMIT 5:最多更新 5 行,但 MySQL 不保证按什么顺序选这 5 行(除非加ORDER BY) -
DELETE ... LIMIT同理,且在事务中可能引发锁范围意外扩大(比如命中 1000 行,但只删 10 条,其余 990 行仍被锁住直到事务结束) -
INSERT ... SELECT可以用LIMIT控制插入条数,但要注意目标表主键冲突时,整条语句可能失败,不会“插一半”
真正难的从来不是写 LIMIT,而是判断该不该用、用在哪、配合什么索引、以及怎么让分页不随着数据量增长而崩掉。游标分页看着多写一行 WHERE,但省下的 IO 和锁竞争,上线后才看得见。










