search 2013 adfgs
作者:Sky.Jian | 可以任意转载, 但转载时务必以超链接形式标明文章原始出处 和 作者信息 及 版权声明
链接:http://isky000.com/database/sharding_groups_global_pk | del.icio.us | Twitter it

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

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

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

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

, ,

已经有12个回复

  1. victor666666 Says @ 09-03-16 4:24 pm

    我提一个:

    每个点的起点不同如100000000
    200000000
    300000000
    :
    :
    步长相同

  2. 朝阳 Says @ 09-03-16 10:49 pm

    @victor666666
    这个不正是我上面所列方案中的倒数第二个么,呵呵

  3. binzhang Says @ 09-03-17 1:19 pm

    最后一个方法实现最简单

    还有就是,现在分N个,以后压力大了,还需要多分M个。 怎么实现动态路由?可能需要引进多个存储 ID–Database_ID 这样的中间数据库。

  4. 朝阳 Says @ 09-03-17 8:49 pm

    @binzhang
    现在的N个就需要为以后考虑,
    现在的N就应该是这个数据库的最大集群数量,可以设置一个几年内不可能达到的集群数量。

    任何一个架构都不可能永远适用,都有其适用阶段,只要满足了该适用阶段就可以了。一般来说,对于一个成长迅速的应用系统,其现有架构在3-5年之后肯定是需要改造的,如果不需要改造,那么上面的N肯定也会足够。

  5. Alex Says @ 09-03-24 5:48 pm

    只有最后一个是被验证用在大规模站点的,问题是如何在应用程序上保证数据分布平衡,比如ebay 就混合使用了对主键简单取模(ID尾数为1的放到第一 台主机,尾数为二的放到下一台,以此类推),有些是按照ID的区间分割(1-1M、1-2M等等),有些用一个查找表. 另外这也跟你的物理主机分布有关,有的是读操作单独的几台组成集群,然后replication 到相关的slave 上,所有slave 前端架mysql proxy 之类的,所有的负载是平衡的,没有一台主机过重就够了.

  6. 朝阳 Says @ 09-03-24 9:20 pm

    @alex
    如何保证数据分布的平衡其实已经有很多成熟的解决方案了,比如对某个字段的hash分区,某个特别字段的范围分区等等。
    至于你所列举的ebay的例子,其实本身和这篇文章中的最后一个解决方案有所冲突。因为这种做法需要在插入之前就已经知道这个主键的值了。但本文最后一个解决方案其实是在插入某个集群之后才知道主键值的。当然,与你所举例子的最终效果是一样的。

  7. cauherk Says @ 09-04-28 2:20 am

    我们的系统是这样解决的。
    1、在数据库层,建立一张专门的表,姑且叫做database_sequence,只有一行记录,标明数据库的顺序ID,写一个函数,查询的输入是sequence的名字,返回的最后一位由data_sequence决定。

    2、经过包装的函数性能比真实的sequence性能会低,那么在应用层,专门设立一个函数,比如叫做,getNextId(name),根据name和配置,在app层,缓存一定步长,根据需要,插入数据量频繁的,步长长一点;不频繁的,步长少一点,会提升很多性能,并且降低数据库的连接并发量和CPU开销。
    3、缺点,app重启的时候,会有部分丢失。数据库重启的话,sequence本身也是会丢失的,除非不用cache。

  8. whnp Says @ 09-11-11 12:54 am

    http://www.mysqlab.net/blog/2009/03/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8sharding%E5%90%8E%E4%B8%BB%E9%94%AE%E5%85%A8%E5%B1%80%E5%94%AF%E4%B8%80%E6%80%A7%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/
    有人 应用层这样实现的 不知道是否可行

  9. 黄兵 Says @ 11-07-11 3:01 pm

    说的很有道理。 请教您一个问题,如果我是已经部署在Oralce的数据,怎么做这个集群唯一主键呢?

Trackbacks & Pingbacks

看完了要说点啥么?