数据库分库分表后,我们怎么保证ID全局唯一( 二 )

  • 10位机器ID
  • 12位序列号

数据库分库分表后,我们怎么保证ID全局唯一

文章插图
 
  1. 我们从上图中可以看出snowflake算法的第二部分的41位时间戳 , 大概可以支撑2^41/1000/60/60/24/365 年 , 也就是大约有69年 。我们设计一个系统用69年应该是足够了吧 。
  2. 10位的机器ID我们可以怎么使用呢?我们可以划分成大概2到3位IDC,也就是可以支撑4到8个IDC机房;然后划分7到 8 位的机器ID , 即可以支撑128~256台机器 。
  3. 12位的序号 , 就代表每个节点每毫秒可以生成4096个ID序号 。
如何改造我们现在已经知道了Snowflake 算法的核心原理 , 并且知道了其有64位的二进制数据 , 那我们就可以根据自己业务进行改造以更好的来为我们业务服务 。一般不同的公司对其进行改造的方式都不尽相同 , 但是道理都是一样的 。我们可以这么做:
  1. 我们是减少序列号的位数 , 增加机器ID的位数 , 是为了用来支撑我们单IDC的更多机器 。
  2. 将我们业务ID加入进去用来区分我们不同的业务 。比如 , 1位0 + 41位时间戳 + 6位IDC(64个IDC) + 6位业务信息(支撑64种业务) + 10位自增序列(每毫秒1024个ID)

数据库分库分表后,我们怎么保证ID全局唯一

文章插图
 
如此 , 我们就可以在单机房部署这么一个统一ID发号器,然后用Keeplive 保证高可用(对于高可用不熟悉的回去看看哈「高可用」你们服务器挂了怎么办 , 我们是这样做的)可以将不同的业务模块ID加入进去 , 这样的好处是即使哪个业务出问题了 , 我只看ID号我就分析出来 , 比如 , 我看到现在ID号有我的订单ID业务 , 我就去看订单模块 。
开发如何使用现在我们知道Snowflake 算法原理了 , 还知道了我们可以进行改造了 。那我们开发人员该怎么去使用 , 来为我们业务生成统一的唯一ID呢?
1 , 直接嵌入到业务代码
嵌入业务代码的意思就是 , 这个snowflake算法就部署在和我们业务相同的服务器上 , 这样我们代码使用的时候 , 就不用了跨网络调用 , 性能相对比较好 。但是也是有缺点的 , 因为我们的业务机器肯定是很多的 , 这就意味着我们发号器算法需要更多的机器ID位数 。同时 , 太多的业务服务器我们会很难保证业务机器id的唯一性 , 这里就需要引用zookeeper一致性组件来保证每次机器重启都能能获得唯一的机器ID 。
2 , 独立部署成发号器服务
也就是说 , 我们将其作为单独的服务部署到单独的机器上 , 已对外提供服务 。这样就是多了网络的传输 , 不过影响不大 , 比如 , 我可以将其部署成一个主备的方式对外提供发号服务 , 机器ID可以用作序列号使用 , 这样也就是会有更多的自增序号 , 有部分大厂就是以这样单独的服务提供出来的 。
开发中避坑大法1 , 虽然snowflake很优秀 , 但是它是基于系统时间的 , 万一我们系统的时间不准怎么办 , 就会造成我们的ID会重复 。那我们的做法就是 , 要利用系统的对时功能 , 一旦发现时间不一致 , 就暂停发号器 , 等到时钟准了在启用 。
2 , 还有一个坑比较关键 , 也是常发生的 , 就是当我们的QPS并发不高的时候 , 比如每毫秒只生成一个ID号 , 这样就是直接结果是 , 每次生成的ID末尾都是1 , 这样我们分库分表就会出现问题呀对吧 , 因为我们用这个ID去分库分表呀 , 会造成数据不均匀 , 是吧 , 忘记了去复习哈(数据库分库分表方案 , 优化大量并发写入所带来的性能问题)那我们怎么解决呢?
我们可以将时间戳记录从毫秒记录改为秒记录 , 这样我一秒可以发好多个号了
生成的序列号起始号随机启动 , 比如这一秒起始号是10 , 我下一秒随机了变成了28 , 这样就更加分散开了 。
总结 , 今天我们针对分库分表之后带来的第一个直接影响我们开发的问题 , 就是主键ID唯一性的问题 , 然后说到了使用Snowflake算法去解决 , 并且对其原理和使用进行了详细的讲解 , 同时 , 还将其在使用中遇到的坑给讲出来了 , 也对其进行了填坑分析 , 让大家直接避免遇到同样的问题 。当然生成唯一ID有多种 , 我们根据业务选择合适我们自己的就好 , 你们是基于什么方式生成的可以也可以告诉大家 。


推荐阅读