MySQL优化实战:冷热分离

6,435 阅读4分钟

大家在用淘宝,饿了么,美团等app的时候,相信都看到过历史订单中,只能查询到最近几个月的订单。这是因为,随着业务发展,数据库增长的很快。数据量达到几千万,甚至上亿。这么庞大的数据量,让平台的订单查询非常缓慢,我之前也说到过,千万的表查询就已经很慢了。这时候要是操作人员手欠,多点几次,CPU飙升,请求阻塞,最后宕机。所以,这个时候就有一种优化手段,叫做冷热分离,也就是大家在app中看到的,只能查询最近几个月的数据(热数据)想要查询冷数据就不能在你的app上查询到了。

什么是冷热分离

顾名思义就是分成两个库,一个是冷库一个是热库,几个月之前不常用的数据放到冷库中,最新的数据比较新的数据放到热库中。这里其实就和JVM里面的新生代和老年代是类似的,有些数据经常用的,最终会放到老年代,不经常用的数据会在新生代GC的时候就会销毁,没GC的时候会存在新生代中

什么情况下用冷热分离?

  1. 旧的数据不在会修改,也就是只有读没有写的情况下
  2. 用户不在意老的数据,老的数据对用户来说没有什么用的情况下,如订单,学校管理系统中,班任带的之前届的学生等。

如何设计冷热数据分离?

以订单为例,我们只需要判断下单时间和是否完成就行了。比如,一个订单是大于半年且已完成的订单,那这个订单其实就可以认为是冷数据,一个用户不可能去找申请退款半年之前的订单吧,那只能说这用户是碰瓷的,那更得阻止这种用户让他查不到半年之前订单。当然,是半年还是三个月还是一个月,这就得看你们实际场景了。也可以根据其他条件,比如是否完成啊,完成的就算冷数据,不完成就热数据等

如何实现冷热数据分离?

这里其实很多人都会想到用监听数据库变更日志binlog的方式来触发,但是这种方式的话,阿里云rds是没有super权限的。而且也无法按照时间来区分冷热,当数据为冷数据,期间没有进行任何操作,同时也需要考虑数据并发问题。

image.png

第二种方式就是通过定时任务:

可以每天半夜进行定时扫描热数据,进行冷热识别,之后同步到冷数据库里面。定时任务也有个缺点就是无法做到实时,并且会出现数据量比较大,一次性处理不完的情况。

image.png

这里我们还需要保证数据的一致性。可以通过分布式事务来保证数据的一致性。但是一删一增比较耗性。还有一种方案:

  1. 在热数据库中,给要搬的数据加个标识:flag=1。(实际处理中标识字段的值用数字就可以,这里是为了方便理解。)
  2. 找出所有待搬的数据(flag=1):这步是为了确保前面有些线程因为部分原因失败,出现有些待搬的数据没有搬的情况
  3. 在冷数据库中保存一份数据,但在保存逻辑中需加个判断以此保证幂等性(这里需要用事务包围起来),通俗点说就是假如我们保存的数据在冷数据库已经存在了,也要确保这个逻辑可以继续进行
  4. 从热数据库中删除对应的数据

针对定时任务方式,假设数据量比较大,一次性处理不完,怎么办?

这里可以结合缓冲区的理念,我们进行分批执行,比如,一百条来执行

  1. 在热数据库中给要搬的数据加个标识:flag=1;
  2. 找出前 50 条待搬的数据
  3. 在冷库中保存一份数据
  4. 从热库中删除对应数据
  5. 循环执行

同时,如果实在太大,我们也可以开启多线程并发执行。但是这就需要好好设计并发代码了。

冷热分离的不足:

  1. 用户查询冷数据还是很慢
  2. 业务无法修改冷数据,冷数据量大了之后需要通过分库分表来解决