目录
一、什么是数据倾斜?
数据倾斜是指在分布式计算或数据库环境中,数据分布不均匀的现象。在理想的分布式系统中,数据和计算负载应该均匀分布在所有节点上。然而,由于各种原因,某些节点可能承载比其他节点更多的数据或计算负载,这就是数据倾斜。
比如一个系统中共有500万数据,但是属于同一个商家的数据就有400万,那么如果数据库按照商家做分库分表,就会导致出现严重的数据倾斜。
一般来说,数据倾斜发生在分库分表的场景中比较多,因为主要是因为分表字段选择的不够合适导致的。你比如说我之前做过一个定价系统,然后我们是按照付款方做的分库分表。原来都挺好的,同一个用户的付款也不会有什么特别多的数据,但是后来出现了一种机构付款的情况,有一个商户作为付款方了,那么就会导致这个商户的数据有很多,就会大量的堆积在同一张表中,就导致这个表数据量剧增。
二、数据倾斜会带来哪些问题?
数据倾斜主要会带来以下几个问题:
-
性能瓶颈:数据倾斜可能导致某些节点的负载明显高于其他节点,从而成为整个系统的性能瓶颈。比如在Redis中,出现的热key问题,其实也是数据倾斜的一种具体情况,那么就会导致这个节点的负载非常高。
-
资源利用不均:导致资源利用不均衡,一部分节点可能过载,而其他节点则闲置。
-
查询效率低下:在数据库中,数据倾斜可能导致查询效率低下,特别是在执行JOIN操作或聚合查询时。比如数据库分表后数据倾斜,就会使得分表的效果并不明显,单表的数据量还是可能会很大,导致查询速度变慢。
-
影响其他业务:某些数据倾斜会导致查询比较慢,这样不仅使这个业务有影响,和他在同一张表中的其他业务的数据也会有影响。比如某个小商家和一个大商户的数据在同一张表,那么它的查询也会变慢。
所以,我们在做分库分表的时候,在选择分表字段的时候,一定要考虑数据倾斜的问题,尽量选择那种不会有聚集性数据的字段来做分表字段,如订单表,尽量选择买家ID。
三、如何解决?
要解决数据倾斜的问题,其实和解决单表数据量大是同样的方案,无非就是把数据拆分一下,分散数据量。
3.1 拆分
比如在Redis中,为了解决热key的问题,可以采用Cluster模式,把一个大key拆分到多个实例上存储。
还有就是在分库分表场景,那就是做一下二次分表。
比如我之前提的那个定价记录表付款方严重倾斜的问题,为了解决它,我修改了一下我的路由算法。如果付款方式机构类型,那么我在分表时就会把时间因素考虑进去。基于时间在做一次分表。
// 为了解决机构账户的热点问题,这里对于付款方是机构的情况做特殊处理,拼接时间戳后进行分表
switch (customerTypeEnum) {
case INSTITUTION_NAME:
externalId = quotationDO.getPayerId() + DateUtils.truncate(quotationDO.getBizTime());
break;
default:
externalId = quotationDO.getPayerId();
}
// 这里再基于externalId做哈希取模
或者如果付款方一样,那么就基于收款方再做一次分表。这样后续每一次的插入、查询、更新都用同一个分表算法就行了。算法内部自己把这个逻辑包掉就可以了。
只不过在查询的时候,就需要的信息就会多一些,以前可能根据一个付款方就够查询了,因为只需要这一个字段就能知道会路由到哪张表,但是修改之后,就需要付款方、付款方类型以及时间戳三个字段了。就会使得查询更加麻烦一些,当然也不是所有查询都需要,只有热点账户相关的才需要,非热点的并不需要。
3.2 隔离
除了做二次分表或者数据拆分以外,还有一个办法,为了降低因为数据倾斜而带来的影响,有的时候我们也会采取物理隔离的方式。
所谓物理隔离,其实就是把这个严重倾斜的商户的数据,单独独立出来放到一个单独的数据库中。
这样既可以降低对其他用户的影响,又可以单独给这个数据库增加配置,提升可用性。
这样做只不过会带来一些额外的成本,并且需要应用在数据库访问的时候动态决策具体去哪个表中做查询,在分表算法中需要做一些定制逻辑。