这是一个比较典型的考察分表算法的问题,也是分库分表中一个经典的数据倾斜问题的解决。
这种问题,一般有两种方案:
1、换一个更加均匀的分表键或者算法
2、人工干预分表算法
这是最常用的方案,当我们用城市进行分表的时候,发现不均匀的时候,那就可以考虑不要用城市字段来分表了,如果业务上没有特定要求,你就可以按照其他的字段来分表,比如按照出生的月份。
但是这个方案有个局限性,那就是如果业务有要求,比如按照城市分,那就不行了。
所谓人工干预,并不是说我们需要每次查询、插入的是都需要人工去查。而是说我们不直接使用简单的取模算法去计算分表结果,而是自己定义一个合适的分表算法。
比如这个场景,就有几种方式来实现了。
方式一:我们设定一个阈值,当城市人口数超过某个阈值的城市我们把他定义为人口密集型城市,然后维护一个列表,一般来说都不太会需要变化。然后在分表算法中,针对人口密集型城市走单独的分表算法。而人口不密集的城市走另外一套分表算法。
假设北京、上海、广州、深圳这四个城市是人口密集型城市,那么就给他们单独创建4张分表,分别存这四个城市的数据,然后其他的城市,共用4张表。伪代码如下:
if 人口密集型城市?
if 北京
return "table_0000";
if 上海
return "table_0001";
if 广州
return "table_0002";
if 深圳
return "table_0003";
else
int index = 城市ID % 4 + 4;
return "table_" + index;
当然,如果除了北上广深之外,还有城市人口也挺多,比如杭州、南京啥的,你就可以在算法中再加一个策略,把固定的几个城市放到同一张特定的分表中。总之就是提前计算好,然后制定一个固定的分表算法。
方式二:同样是针对城市区分出人口密集型和不密集型,然后针对密集型城市,想办法针对他进行二次拆分,比如说北京城市人口比较多,那么我们就可以针对北京的数据再拆分一次,比如把朝阳区的单独拆出来。
这种就可以用通用的方式,比如原来是城市 ID
取模,那么针对密集型城市,就用城市 ID + 区 ID
取模 。伪代码如下:
if 非人口密集型城市?
int index = 城市ID % 8;
return "table_" + index;
if 人口密集型城市?
int index = (城市ID + 区ID) % 8;
return "table_" + index;
这样就使得北京的用户可以被分散到更多的表中,而不是一张表,就可以更加的均匀了。
首先一看到这个问题,估计很多人会想到轮询,为了均匀。。。
但是轮询的方式很扯淡,因为分表算法不仅仅是在插入的时候要用,查询的时候也要用,如果你插入的时候用了轮询了,那么查询咋办?也轮询吗?那分表的意义是什么呢?
所以,轮询的方式不合理,也基本没人这么用。