一般来说,在很多大厂中,需要做分表的时候,分表的数量都会选择2的幂,比如16、64、128、512、1024等,这么做有什么好处呢?
首先,如果你看过 HashMap 的 hash 算法的话,应该知道,HashMap 的容量其实也是2的幂,这么做的好处在下面的文章中讲过,那就是可以讲取模操作改为更加高效的位运算。
因为,hash % 8 相当于 hash & (8-1),所以对于分8个表,可以使用哈希值与7进行按位与操作,这不仅简化了计算,还提升了性能。
所以,使用2的幂作为分表数量的第一个好处,就是可以将取模算法优化成位运算算法。
除了这个好处以外,我们通常分表都是伴随着分库的,比如我们分16个库128张表, 这样如果我们的分表数量和分库数量都是2的幂,那么就可以实现均匀分布。128张表就可以均匀的分到16个库中,每个库中有8张表。
所以,使用2的幂作为分表数量的第二个好处,就是可以使得多张分表在多个库中均匀分配。
当我们在做分库分表的时候,必然要考虑的一个问题那就是二次分表的问题,比如我们根据当前的业务发展,计算出可能需要分4张表就够了,但是随着业务增长,可能认为需要更多表才行,这时候如果我们把新的分表数量定位8张表,那么在做数据迁移的时候,就可以只迁移部分数据。
假设我们最开始将订单表分成了4张表,分别是 order_00、order_01、order_02、order_03,这时候假设我们的分表算法是根据 userId取模。即 userId % 4
order_00:userId % 4 == 0 order_01:userId % 4 == 1 order_02:userId % 4 == 2 order_03:userId % 4 == 3
当数据量增长,需要将表扩展到8个时,需要将算法改为userId % 8 :
新的哈希值计算:
order_00:userId % 8 == 0 order_01:userId % 8 == 1 order_02:userId % 8 == 2 order_03:userId % 8 == 3 order_04:userId % 8 == 4 order_05:userId % 8 == 5 order_06:userId % 8 == 6 order_07:userId % 8 == 7
而在做储量数据迁移的时候,我们只需要重新分配那些原来在表0、1、2、3中,且哈希值满足 userId % 8 >= 4 的数据进行迁移就好了,userId % 8 的结果在0-3范围内的数据其实是不用动的。
从表0迁移到表4:如果 hash % 8 == 4 从表1迁移到表5:如果 hash % 8 == 5 从表2迁移到表6:如果 hash % 8 == 6 从表3迁移到表7:如果 hash % 8 == 7
也就是说,从4张表扩容到8张表其实只需要迁移一半的数据。而如果不是2的幂,比如说从5张表扩容成9张表,那每个原数据都需要重新计算哈希值并重新分配到新的表中。因为扩展后的表数量不是2的幂次,大多数数据都会被重新分配到不同的表中。
所以,使用2的幂作为分表数量的第三个好处,也是最重要的一个好处,那就是在做数据迁移时只需重新计算和迁移一半数据。这种方法不仅降低了系统扩展的复杂性,还减少了扩展过程中对系统性能的影响。