亲,这款游戏可以开挂的,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到-人的牌一样。所以很多小伙伴就怀疑这...
2025-08-06 0
如果你已经有过几轮面试经验,肯定会遇到过类似的问题。
面试官会问你:“如果数据量特别大的时候,分页查询慢该怎么办?”
我们都知道,数据库分页查询很常见,尤其是展示列表的功能,基本上每个系统都有。
但是,如果你做过大流量、高并发的系统,就会知道,当数据量上来后,分页查询的性能问题真的是一大痛点。
数据量小的时候,分页查询没问题,但是当表里有上千万条数据时,翻一翻第几页,查询就开始卡了,慢得让人想砸电脑。
那面试官提这个问题的时候,肯定希望听到你对于分页查询慢的根本原因是什么,知道你是怎么解决这个问题的,以及分库分表后深度分页的问题该如何解决?
所以,小北今天就用最简单的语言,带大家逐步分析这个问题,看看怎么从根本上解决这个痛点。
最重要的是,让面试官觉得你是个能打硬仗的技术高手,给你点个赞,甚至直接发出offer!
为什么分页查询随着翻页的深入,会变得越来越慢。
其实,问题的根本就在于:
你以为LIMIT 100000,10是直接跳过后10万条?太天真了!
数据库的真实操作:
相当于:让你从新华字典第1页开始翻,翻到第1000页才找到字,谁能不炸?
最坑爹环节:回表查数据
如果用了普通索引(比如按年龄建的索引):
10万次回表 = 10万次IO操作,不卡你卡谁?
再说另一个常见的情况——排序。
大多数时候,分页查询都会带有排序,比如按时间、按ID排序。
数据库不仅要查数据,还得根据你的排序要求重新排一次,特别是在数据量大的时候,排序的开销就变得非常大。
所以,翻越几百页的时候,你的查询可能就开始慢得像蜗牛。
核心思路: 绕过全表扫描,直接定位到目标数据!
-- 先查索引定位ID,再捞数据(54毫秒搞定!)SELECT * FROM user WHERE id >= (SELECT id FROM user ORDER BY age LIMIT 100000, 1)LIMIT 10;
原理: 用覆盖索引快速找到第100000条的ID,直接从这个ID开始拿数据,跳过前面10万次回表。
缺点是,不适用于结果集不以ID连续自增的分页场景。
在复杂分页场景,往往需要通过过滤条件,筛选到符合条件的ID,此时的ID是离散且不连续的。如果使用上述的方式,并不能筛选出目标数据。
SELECT * FROM user t1JOIN (SELECT id FROM user ORDER BY age LIMIT 100000,10) t2 ON t1.id = t2.id;
原理: 先用索引快速拿到10个目标ID,再一次性联表查完整数据,减少回表次数。
索引覆盖(Index Covering)是指一个查询可以完全通过索引来执行,而无需通过回表来查询其他字段数据。
例如:
ALTER TABLE user ADD INDEX idx_age_name(age, name); -- 查询+排序全走索引SELECT age, name FROM user ORDER BY age LIMIT 100000,10; -- 0.1秒!
精髓: 索引里直接存了所有要查的字段,不用回表,直接起飞!
假设订单表分了3个库,每个库分了2张表(共6张表),按用户ID分片。
当你执行:
SELECT * FROM orders ORDER BY create_time DESC LIMIT 1000000, 10;
你以为数据库的操作:
智能跳过100万条,从6张表各拿10条,合并完事?
实际上的操作:
1、 每张表都老老实实查100万+10条数据(共600万+60条);
2、 把所有数据汇总到内存,重新排序(600万条数据排序,内存直接炸穿);
3、 最后忍痛扔掉前100万条,给你10条结果;
结果: 查一次耗时10秒+,数据库CPU 100%!
一句话总结: 分库分表后,翻页越深,死得越惨!
核心思想: 别让用户随便跳页,只能一页一页翻!
1、 第一页查询:;
-- 按时间倒序,拿前10条 SELECT * FROM orders WHERE user_id = 123 ORDER BY create_time DESC LIMIT 10;
1、 翻下一页:;
-- 记住上一页最后一条的时间 SELECT * FROM orders WHERE user_id = 123 AND create_time < '2023-10-01 12:00:00' -- 上一页最后一条的时间 ORDER BY create_time DESC LIMIT 10;
优点:
缺点:
核心思想: 把分库分表的“大海捞针”,变成“精准狙击”!
1、 第一轮查询:每张分片查缩小范围的数据;
-- 每张分片查 (offset / 分片数量) + limit 条 SELECT create_time FROM orders ORDER BY create_time DESC LIMIT 33334, 10; -- 假设总offset=100万,分6个分片:100万/6 ≈ 166666
1、 确定全局最小时间戳:;
从所有分片结果中,找到最小的 create_time (比如 2023-09-20 08:00:00 )。 2、 第二轮查询:根据最小时间戳查全量数据;
SELECT * FROM orders WHERE create_time >= '2023-09-20 08:00:00' ORDER BY create_time DESC LIMIT 10;
优点:
缺点:
核心思想: 让专业的人干专业的事!
1、 写入时:订单数据同时写MySQL(分库分表)、ES、HBase;
2、 查询时:;
GET /orders/_search { "query": { "match_all": {} }, "sort": [{"create_time": "desc"}], "from": 1000000, "size": 10 }
List<Order> orders = es.search(...); // 从ES拿到10个ID List<Order> details = hbase.batchGet(orders); // 从HBase拿详情
3、 Step2:用ES返回的ID,去HBase批量查数据;
4、 Step1:用ES查分页(只查ID和排序字段);
优点:
缺点:
“分库分表后深度分页的难点在于全局排序和内存压力。
我们有三种方案:
1、 禁止跳页:适合C端Feed流,用连续查询代替跳页;
2、 二次查询法:通过两次查询缩小范围,适合管理后台;
3、 ES+HBase:扛住亿级数据分页,适合高并发大厂场景;
在实际的场景中,订单查询需要支持搜索条件,我们最终用ES+HBase,性能从10秒降到50毫秒。”
加分的骚操作:
分库分表后的深度分页,本质是 “分布式数据排序” 的难题。
最后一句忠告:
面试被问分页,先拍桌子喊出“禁止跳页”,再掏出ES,面试官绝对眼前一亮!
相关文章
亲,这款游戏可以开挂的,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到-人的牌一样。所以很多小伙伴就怀疑这...
2025-08-06 0
您好:这款游戏是可以开挂的,软件加微信【添加图中微信】确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到其他人...
2025-08-06 0
亲,这款游戏可以开挂的,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到-人的牌一样。所以很多小伙伴就怀疑这...
2025-08-06 0
发表评论