|
那晚,大约晚上 11 点,我与 Chaya 在丽江的洱海酒店享受两人世界的乐,电商平台的运维大群突然炸开了锅。
监控系统发出刺耳的警报:订单查询接口响应时间从200ms 飙升到 12 秒,数据库 CPU 利用率突破 90%。
发现事故根源竟是一个看似平常的查询――用户中心的历史订单分页查询。
这背后隐藏的正是MySQL 深度分页的典型问题――数据越往后查,速度越让人抓狂。
其本质是传统分页机制在数据洪流下的失效:LIMIT 100000,10这样的查询,会让数据库像逐页翻阅千页文档的抄写员,机械地扫描前 10 万条记录再丢弃。
当数据量突破千万级时,这种暴力扫描不仅造成 I/O 资源的巨大浪费,更会导致关键业务查询的链式阻塞。
本文将深入拆解深度分页的技术黑箱,通过电商订单表等真场景,揭示 B+树索引与分页机制的碰撞奥秘,并给出 6 种经过战检验的化方案。
深度分页
假设电商平台的订单表存储了 2000 万条记录,表结构如下,主键是 id,(user_id + create_time )联合索引。
复制
REATE TABLE `orders` (
`id` int NOT NULL AUTO_INCREMENT, -- id自增
`user_id` int DEFAULT NULL,
`amount` decimal(10,2) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP, -- 创建时间默认为当前时间
PRIMARY KEY (`id`),
KEY `idx_userid_create_time` (`user_id`, `create_time`) -- 创建时间设置为普通索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1.
2.
3.
4.
5.
6.
7.
8.
我们的分页语句一般这么写。
复制
SELECT * FROM orders
WhERE user_id = 'Chaya'
ORDER BY create_time DESC
LIMIT 0, 20;
1.
2.
3.
4.
当用户查询第 1000 页的订单(每页 20 条),常见的分页写法如下。
复制
SELECT * FROM orders
WhERE user_id = 'Chaya'
ORDER BY create_time DESC
LIMIT 19980, 20;
1.
2.
3.
4.
执行流程解析:
使用联合索引 idx_userid_create_time读取 19980 + 20 条数据。
利用索引在内存中排序。
丢弃 19880 条数据,返回剩下的 20 条。
随着页码增加,需要处理的数据量会线性增长。当 offset 达到 10w 时,查询耗时会显著增加,达到 100w 时,甚至需要数秒。
游标分页(Cursor-based Pagination)
适用场景:支持连续分页(如限滚动)。
现原理:基于有序且仅有的字段(如自增主键 ID),通过记录上一页比较后一条记录的标识(如主键 ID),将WHERE条件与索引结合,跳过已查询数据。
复制
-- 首页
SELECT *
FROM orders
WhERE user_id = 'Chaya'
ORDER BY create_time DESC
LIMIT 20;
-- 后续页(记录上一页查询得到的 id,id=1000)
SELECT id, user_id, amount
FROM orders
WHERE id > 1000 AND user_id = 'Chaya'
ORDER BY create_time DESC
LIMIT 20;
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
索引树直接定位到order_id=1000的叶子节点,仅扫描后续 1000 条记录,避免遍历前 100 万行数据。
势
完全避免 OFFSET扫描,时间复杂度从 O(N)降为 O(1)
天然支持顺序分页场景(如限滚动加载)
限制
不支持随机跳页(如直接跳转到第 1000 页)
需保证排序字段仅有且有序
结合这些条件来看,知识图谱和图数据库依然能够让我们看到很好的发展,为整个市场带来鲜活的生命力。悦数图数据库是一款完全自主研发的国产图数据库和原生分布式图数据库,具有高性能,易扩展,安全稳定,自主可控的特点.万亿级数据仅需毫秒级查询延时,应用于金融风控,实时推荐,知识图谱等业务场景。https://www.yueshu.com.cn/
|
|