目录
一.冷热分离概念:
二.解决方案:
三.具体实现思路:
四.难点:
业务背景:系统在使用的过程中随着业务数据量越来越多,已经超过了数据库中单表的承受能力,系统的瓶颈在数据库IO上,这时候可以通过冷热数据分离的方式来解决查询速度慢的问题。
数据库IO瓶颈的解决方案除了冷热分离,还有读写分离,分库分表等方案。但读写分离还是无法解决单表数据量大导致的查询速度慢的问题,当数据量小于500w(这里的数据量受表字段多少,字段长度的影响,不一定是500w,虽然mysql官方说是500w,但是如果字段较少1000w时才感觉数据库查询速度慢,如果字段多可能100w就感觉数据库 查询速度慢了。),这时候可以采用读写分离的方案,毕竟方案简单,只需要把读操作放到从库即可。如果数据量很大,比如2000w,甚至上亿的数据量,如果数据是有明显的时间或状态特征,就可以考虑使用冷热分离的方案(有些人也称为历史表方案),这种方案相对于分库分表的方案来说还算简单点,毕竟是把热点数据放到热库中,热库可能就只有不到百万的数据,冷库可以根据时间和状态分为多个冷库。如果是千万以上甚至亿级数据量,但是这些数据可能都是经常常用的,可以直接考虑分库分表,特别是水平分库分表,通过哈希取模的方案,把数据均匀的落在各个库中。本文主要介绍冷热分离方案的思路。
一.冷热分离概念:
指的是把常用数据和非常用数据分开存放到多个表或库中,避免单表数据量过大,最终提高查询效率。
热数据特征:一般一般都是中间状态,非终审状态,有读的需求,也有写的需求。一般来说时间都是在最近一段时间,且业务状态还没结束的数据。
冷数据特征:一般存放数据走到终审状态,没有中间状态的数据,只有读的需求没有写的需求。一般来说时间都是在以前的时间,业务状态一般都是已完结状态的数据。
二.解决方案:
1.可以在代码上根据业务代码的情况,如时间,业务状态等,把由热数据转为冷数据的这部分数据写入到冷库中。
2.定时扫描数据库的方式,写定时任务的方式做冷热分离。其实这种方式与方式一也很类似,但有个优点是能与业务代码解耦,夜间执行定时任务的方式减少了白天系统的压力。
3.通过中间件(如canal)监听mysql数据库的binlog日志,后做冷热数据分离。
小结:实际上这几种方式都有可行性,可以根据团队的情况选择某种方式实现即可,比如如果是小项目,连分布式定时任务如xxlJob都没有的情况下,也没有引入canal等中间件的情况下,那就只能用方式一了。如果是已经引入了定时任务,但是却没引入canal等中间件,就可以使用方式二。如果项目团队成员对canal中间件很熟悉,就可以直接使用方式三了。
三.具体实现思路:
1.判断冷热数据
2.将冷数据写入到冷库
3.在热库中删除冷数据
四.难点:
1.一致性,如何保证冷热库的数据是一致的。
可以在热库中增加一个状态的标识,表示比如0表示无需迁移,1表示需要迁移到冷库。然后在判断冷热数据后修改这个状态,然后把需要迁移的数据写入到冷库,等冷库写入(冷库写入前可以查询一下是否已经在冷库)成功后,再去根据冷库中查询出的数据去热库删除这种数据。在这个过程中要处理好数据迁移失败时如何回滚,即数据可以多次执行,多次执行后还是正确的数据。如果冷库数据量非常大,再次查询的方式很慢,影响效率,可以考虑使用redis存储好key,做好幂等性校验就行了。
2.数据量问题。
一般来说需要进行迁移的数据量如果到了几万一天甚至十万一天的时候,一条条处理是非常慢的,可以考虑使用批量处理的方式,比如一次性查出100条数据,然后拼接好insert语句,注意别使用prepareStatement 预处理的方式也就是?号占位符的方式,而是直接用statement的方式是无需拼接?占位符的方式,这样的处理效率比较高(占位符越多,意味着解析替换要花费的资源就越多,在能够保证安全的情况下用statement的方式较好)。
3.数据量超大问题。
数据量已经超过了单线程的的批量insert处理能力时,也就是我们上面说的那种情况。可以考虑使用多线程处理,一般来说需要迁移的数据相互之间都是没联系的,比如通过计算之后发现一天需要处理20万的数据,然后需要在10分钟内迁移完成,可以先计算一下需要多少个线程,然后每个线程都只是分别查询出自己需要迁移的数据(可以用乐观锁(版本号)来做标记)。如果处理失败或者锁超时后,可以重新执行就好了。