MySQL深分页 + 多字段排序场景的优化方案【三百万级数据量】

点击上方“Java基基”,选择“设为星标”

做积极的人,而不是积极废人!

每天 14:00 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:blog.csdn.net/qq_43097201/

article/details/128773909


需求背景

目前产品需要针对一个大范围地区内的所有用户做排行榜功能,且这个排行榜有几个比较蛋疼的附加需求:

排行榜需要全量展示所有用户,且做分页展示(大坑💥)

5c89cf12ab6e2d0ac2ed5643273ca159.png

排行榜有4种排序条件,且每个排序条件都是单独的。例如:用户的应用A下载数、应用B下载数、应用C下载数、应用D下载数(产品不期望把所有的数据整合成一块进行排名)

28901e9fce41b7da968f98224fa70108.png

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

历史代码背景

其实这个需求已经够扯了,雪上加霜的是,以前的开发者在开发排行榜的时候,由于需求背景原因,采用了多表join的方式来进行查询。

这是什么意思呢?这里详细说一下:

  • 假设有一张表C,就是排行榜的单表数;

  • 目前无法直接从表C中拿到排行榜的所有所需字段。

听说是需求原因,别无他法

导致了开发者在MyBatis层面通过多表join的方式补充了所需字段;

最后SQL就是:

table A left join table B left join table C

以上方式进行多表join,先不说分页、排序等性能问题。单纯表A join 表B都已经耗时3秒(表A数据量250W、表B数据量300W)

优化点一:多表join优化

针对多表join的问题,必须想尽办法把多表join的查询操作改为单表查询,否则多表join随着数据量增加,后期性能不敢想象

优化点二:优化SQL,避免深分页所带来的问题

select * from table A t1 join (
 select id from table A 
 order by indexA des 
 limit 20000, 20
) t2 on t1.id = t2.id

这里通过子查询的方式,且限制分页的页数(注意,虽然pageSize = 20000,MySQL会扫描前20000个数据,然后再从20001开始拿数据,再丢弃前20000条数据,因此还是会顺序扫前面的20000条数据,而不是跳到20001开始扫描的。这里可以网上查资料学习下)

此SQL还有优化空间,就是在临时的子表中补充上where条件,就可以直接筛选掉大部分无效数据

select * from table A t1 join (
 select id from table A  
 where index_value > 100000 
 order by indexA des 
 limit 20000, 20
) t2 on t1.id = t2.id
抛出一个问题

MySQL的in和join,谁的性能更好?

在本次开发过程中,in的方式指定查询大量数据,发现DB查询超时了;只有使用join的方式才能查到数据,这是为什么?

优化点三:深分页,设定阈值合理倒序查询

其实深分页最主要的问题就是 limit m, n 这个偏移量的问题;如果正序数,拿最后一页数据,相当于扫描前m个数据,再从m+1开始拿到m+n;

仔细想想,这个地方,如果正序拿最后一页,那不如我直接倒序拿第一页?这样的话,就规避了深分页问题,这个一般会设定一个阈值,超过阈值就进行正序 / 倒序

优化点四:排序字段单独建立索引

其实针对排序字段,一般会补充索引来进行优化,因此多字段排序的话,尽量让每个排序字段都单独设置一个索引,因为索引已经帮我们做好排序了。

这里要注意一个点,尽量不要接多字段同时排序的需求,这种情况下索引的设计将会十分复杂

优化点五:单表拆分

因为表C有多个排序字段,且还有各种where条件筛选,此时如果建立联合索引来解决的话,因为需要满足 最左匹配原则,此时联合索引的数量将会很大,届时索引树也会十分复杂。还好排行榜是读多写少的表数据,否则性能堪忧;

此时其实更建议进行单表的拆分,让每一个表所负责的职责更加明确;因为以前的表C,相当于就是把多个排行榜冗余在一个单表中了,这时候表C的压力是很大的。因此单表拆分,此时针对单表的排序字段建立对应的索引,且单表职责更加单一;

单表拆分方案:

  • 查询某个字段的排序数据时,在MyBatis层面,根据排序字段,指定查询排序字段所对应的单表。

  • 单表拆分后,需要合理创建索引



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

384bb51bf8f8b48317ad4cedb9957b51.png

已在知识星球更新源码解析如下:

8a35844c71c878429d86652b84e37817.jpeg

1b1a9a2d49d86627498b2046a318ec17.jpeg

2b51c0bdd7dc15c317933037829381ca.jpeg

d41c7959caaef06d5752b8d355c0465b.jpeg

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 6W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值