⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/qq_43097201/article/details/128773909 「ADAMs.」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

需求背景

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

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

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

历史代码背景

其实这个需求已经够扯了,雪上加霜的是,以前的开发者在开发排行榜的时候,由于需求背景原因,采用了多表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层面,根据排序字段,指定查询排序字段所对应的单表。
  • 单表拆分后,需要合理创建索引
文章目录
  1. 1. 需求背景
  2. 2. 历史代码背景
    1. 2.1. 优化点一:多表join优化
    2. 2.2. 优化点二:优化SQL,避免深分页所带来的问题
      1. 2.2.0.1. 抛出一个问题
  3. 2.3. 优化点三:深分页,设定阈值合理倒序查询
  4. 2.4. 优化点四:排序字段单独建立索引
  5. 2.5. 优化点五:单表拆分