

你是否遇到过这样的场景?
根本原因:传统的LIMIT offset, size分页方式在大数据量下会产生全表扫描+临时排序,当offset值达到10万量级时,MySQL需要遍历并丢弃前10万行数据才能返回结果。今天给大家分享游标分页与覆盖索引两大核心技术,实测将百万级数据分页耗时从秒级降至毫秒级!
典型的慢查询示例:
SELECT * FROM order_history
WHERE user_id = 100
ORDER BY create_time DESC
LIMIT 100000, 10; 执行计划分析:
type=ALL(全表扫描) rows=100010(实际扫描行数) Extra=Using filesort(文件排序) 三级性能瓶颈:
利用有序唯一值作为定位锚点,避免遍历历史数据:
-- 下一页
SELECT * FROM order_history
WHERE user_id = 100 AND id > 上一页最后一条ID
ORDER BY id ASC
LIMIT 10;
-- 上一页
SELECT * FROM order_history
WHERE user_id = 100 AND id < 当前页第一条ID
ORDER BY id DESC
LIMIT 10; 原始表结构:
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2),
create_time DATETIME,
INDEX idx_user_create(user_id, create_time)
);优化后查询:
-- 第一页
SELECT * FROM orders
WHERE user_id = 100
ORDER BY create_time DESC, id DESC
LIMIT 10;
-- 下一页(假设上一页最后一条create_time='2023-08-20 15:30:00', id=9527)
SELECT * FROM orders
WHERE user_id = 100
AND (create_time < '2023-08-20 15:30:00'
OR (create_time = '2023-08-20 15:30:00' AND id < 9527))
ORDER BY create_time DESC, id DESC
LIMIT 10; 优化效果:
通过索引覆盖避免回表查询,结合延迟关联(Deferred Join) 技术:
SELECT t.* FROM (
SELECT id FROM orders
WHERE user_id = 100
ORDER BY create_time DESC
LIMIT 100000, 10
) AS tmp
INNER JOIN orders t ON tmp.id = t.id; 表结构:
CREATE TABLE user_behavior (
id BIGINT AUTO_INCREMENT,
user_id INT,
action VARCHAR(20),
device VARCHAR(50),
log_time DATETIME,
PRIMARY KEY(id),
INDEX idx_user_log(user_id, log_time)
);优化前后对比:
-- 原始查询(耗时1.2秒)
SELECT * FROM user_behavior
WHERE user_id = 500
ORDER BY log_time DESC
LIMIT 80000, 20;
-- 覆盖索引优化(耗时45毫秒)
SELECT t.* FROM (
SELECT id FROM user_behavior
WHERE user_id = 500
ORDER BY log_time DESC
LIMIT 80000, 20
) AS tmp
INNER JOIN user_behavior t ON tmp.id = t.id; 执行计划变化:
Using index(仅扫描索引) Using where(快速主键检索) SELECT t.* FROM (
SELECT id FROM orders
WHERE user_id = 100 AND id > 上一页最后ID
ORDER BY id ASC
LIMIT 10
) AS tmp
INNER JOIN orders t ON tmp.id = t.id; 按时间范围分区后查询:
-- 按月分区
PARTITION BY RANGE (TO_DAYS(create_time)) (
PARTITION p202301 VALUES LESS THAN (TO_DAYS('2023-02-01')),
PARTITION p202302 VALUES LESS THAN (TO_DAYS('2023-03-01')),
...
);
-- 查询指定分区
SELECT * FROM orders PARTITION (p202307)
WHERE user_id = 100
ORDER BY create_time DESC
LIMIT 10; 将分页查询路由到只读副本:
-- 主库写入
INSERT INTO orders(...) VALUES(...);
-- 从库分页查询
SELECT * FROM orders_slave
WHERE user_id = 100
ORDER BY create_time DESC
LIMIT 100000, 10; 优化维度 | 技术手段 | 适用场景 |
|---|---|---|
查询模式 | 游标分页 | 连续分页(如APP瀑布流) |
索引设计 | 覆盖索引 + 延迟关联 | 复杂排序分页 |
架构设计 | 分区表 + 读写分离 | 超大数据量场景 |
互动问答:
💡 你在项目中遇到过哪些分页性能问题?最终是如何解决的?欢迎在评论区分享你的实战经验!
日常建议:
EXPLAIN分析现有分页查询 LIMIT与游标分页性能差异 让技术产生价值,让数据流畅滑动!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。