数据表水平切分的ID规则

这个问题很突出,所以转过来说一下。

http://www.jianzhaoyang.com/database/sharding_groups_global_pk

现在通过数据的水平切分(sharding)来实现数据库 Scale Out 的解决方案受到了越来越多人的青睐,但是在切分过程中可能遇到的问题也肯定不在少数,如切分规则的设计,切分后的访问路由,切分后的主键的全局唯一等等。

这里我主要列举几个可以使用在 MySQL 数据库主键全局唯一方案及其优劣,供大家参考:

  • 通过应用程序生成一个GUID,然后和数据一起插入切分后的集群。优点是维护简单,实现也容易。缺点是应用的计算成本较大,且GUID比较常,占用数据库存储空间较大,涉及到应用的开发。 
    --------------------------------------------------------------------------------
    说明:主要优势是简单,缺点是浪费存储空间,GUID:32字节,100W记录,多32兆,如果是int,4字节,会少28M。如果有外键关联,会浪费更多。
  • 通过独立的应用程序事先在数据库中生成一系列唯一的 ID,各应用程序通过接口或者自己去读取再和数据一起插入到切分后的集群中。优点是全局唯一主键简单,维护相对容易。缺点是实现复杂,需要应用开发。 
    --------------------------------------------------------------------------------
    说明:ID表要频繁查和频繁更新,插入数据时,影响性能。
  • 通过中心数据库服务器利用数据库自身的自增类型(如 MySQL的 auto_increment 字段),或者自增对象(如 Oracle 的 Sequence)等先生成一个唯一 ID 再和数据一起插入切分后的集群。优点是?好像没有特别明显的优点。缺点是实现较为复杂,且整体可用性维系在这个中心数据库服务器上,一旦这里crash 了,所有的集群都无法进行插入操作,涉及到应用开发。
    --------------------------------------------------------------------------------
    说明:不推荐。
     
  • 通过集群编号加集群内的自增(auto_increment类型)两个字段共同组成唯一主键。优点是实现简单,维护也比较简单,对应用透明。缺点是引用关联操作相对比较复杂,需要两个字段,主键占用空间较大,在使用 InnoDB 的时候这一点的副作用很明显。 
    ---------------------------------------------------------------------------------
    说明:虽然是两个字段,但是这方式存储空间最小,仅仅多了一个smallint两个字节,100W也就多2M。
  • 通过设置每个集群中自增 ID 起始点(auto_increment_offset),将各个集群的ID进行绝对的分段来实现全局唯一。当遇到某个集群数据增长过快后,通过命令调整下 一个 ID 起始位置跳过可能存在的冲突。优点是实现简单,且比较容易根据 ID 大小直接判断出数据处在哪个集群,对应用透明。缺点是维护相对较复杂,需要高度关注各个集群 ID 增长状况。 
    ---------------------------------------------------------------------------------
    说明:段满了,调整太麻烦。
  • 通过设置每个集群中自增 ID 起始点(auto_increment_offset)以及 ID 自增步长(auto_increment_increment),让目前每个集群的起始点错开 1,步长选择大于将来基本不可能达到的切分集群数,达到将 ID 相对分段的效果来满足全局唯一的效果。优点是实现简单,后期维护简单,对应用透明。缺点是第一次设置相对较为复杂。 
    ---------------------------------------------------------------------------------
    说明:会重合吧?

除了上述方案之外,各位网友如果想到什么比较巧妙的解决方案,希望能不吝分享。

==========================================================

上面的各种方案没有考虑,外键关联时,怎么获取数据的问题,比如博客帖子里面保存了用户ID是1,那么用户ID为1的用户存在哪个数据库中呢?通过ID是无法知道的。

  1. 因此,水平切分后,要么,就像上文的第4中,就是集群编号+用户ID做为主外键.则其他表外键关联时就必须关联两个字段:集群编号、对象编号。集群ID可以用TinyInt等只有1个字节的,这样100W只多1M。
  2. 另外的一个方案是改进版本的第5个方案。分段是可行的,int不是4字节,32位么。那么拿出几位来做集群编号。2^6为集群编号(前6位,共64段),2^26为对象ID区域,就是说
        假如集群编号是n(0-n),2^26是常数G,那么开始段为n*G,结束段为(n+1)*G-1
    通过这样处理之后,那么外键关联时,只要取ID的前6位,就知道集群编号了。根据集群编号才能访问数据库啊。每段有2^26,有6kw的数据量,因为64个集群,所以,基本上是够用了,如果每个库的数据量太大,ID数据类型改为BigInt(8字节)之类的也就可以了,要尽量避免数据不够的问题,当然单表超过这个数据量,估计还是增加集群机器数量比较妥当。

你可能感兴趣的:(数据表水平切分的ID规则)