数据库主键生成方案

问题产生的背景:

数据量到达一定程度时,需要做数据的分库分表(这又是一个很值得讨论的问题,什么量级的数据做分库分表),此时我们需要保证数据库主键的唯一性、单调递增。

并且获取唯一主键的服务要保证高可用、高吞吐率。


一般常用的方法有snowflake算法、UUID、数据库自增主键等等。


因为MySql是聚集存储方式,其存储是按照主键顺序存放的,所以实际生产环境会要求主键递增,所以UUID一般用的比较少。

snowflake算法需要依赖机器时钟,所以需要多个机器节点同步时钟,实现复杂。

最为简单的就是数据库自增主键,但是会存在单点问题,并且每次需要读库、写库吞吐率较低。

为了解决这个问题,我们分几步来思考

第一步:最容易想到的,我们部署n个数据库,每个数据库初始值为1、2......n,自增步长为n,这样解决了单点问题,客户端轮训的方式分别在每个库获取主键。

第二步:更容易想到的常规方法,读写分离,增加读性能。所以我们为每个数据库实例做读写分离,master只写,slave只读。为了提高性能,可以采用半同步的方式。

第三步:读写分离会产生主从不一致,并且每次读数据库,耗时会比较高。我们想到可以一次取出一个号码段缓存到本地,每次从缓存中取id,这样可以利用取缓存过度主从不一致的时间,并且不用每次都读数据库,增加性能。但是这样会产生一个问题,id不再是严格递增,只能保证趋势递增了。

第四步:每次缓存取完的时候,都需要访问一次数据库,导致接口耗时的突刺,为了避免这种情况,我们采用双buffer,当其中一个buffer取到一定阈值的时候,后台自动拉取一个新的号码段放到另一个缓存中,当前缓存取完后,直接切换到另一缓存。完全避免了接口耗时突刺的情况。

和redis中的hash数据结构的渐进式rehash思想有异曲同工之妙,redis中hash数据结构就有两个table,在rehash的时候,不是一次性的把所有数据rehash到新的table中,而是在每次对hash表进行操作时,除了正常操作,还需要对相应key进行rehash,把每一次rehash操作平摊到每一次操作中,当然新增操作就直接在新table中增加即可,保证原table只减不增。这样总会有全部rehash完的时候。

redis中有很多设计非常巧妙,如何节省空间(比如string的free,整数集合的升级)、如何增加查找效率(字典、跳跃表),都可以借鉴一下。


通过以上四步,没简单的利用mysql自增主键建立一个分布式主键生成服务,既保证了唯一性、又保证了递增趋势。还具有高可用、高性能。

明天写详细设计。



发布了45 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/ly262173911/article/details/80011171