分布式雪花算法获取id

作者 | MXC肖某某
来源 | urlify.cn/zEB7Vn
正文
实现全局唯一ID一、采用主键自增最常见的方式 。 利用数据库 , 全数据库唯一 。
优点:
1)简单 , 代码方便 , 性能可以接受 。
2)数字ID天然排序 , 对分页或者需要排序的结果很有帮助 。
缺点:
1)不同数据库语法和实现不同 , 数据库迁移的时候或多数据库版本支持的时候需要处理 。
2)在单个数据库或读写分离或一主多从的情况下 , 只有一个主库可以生成 。 有单点故障的风险 。
3)在性能达不到要求的情况下 , 比较难于扩展 。
4)如果遇见多个系统需要合并或者涉及到数据迁移会相当痛苦 。
5)分表分库的时候会有麻烦 。
二、UUID常见的方式 。 可以利用数据库也可以利用程序生成 , 一般来说全球唯一 。
优点:
1)简单 , 代码方便 。
2)生成ID性能非常好 , 基本不会有性能问题 。
3)全球唯一 , 在遇见数据迁移 , 系统数据合并 , 或者数据库变更等情况下 , 可以从容应对 。
缺点:
1)没有排序 , 无法保证趋势递增 。
2)UUID往往是使用字符串存储 , 查询的效率比较低 。
3)存储空间比较大 , 如果是海量数据库 , 就需要考虑存储量的问题 。
4)传输数据量大
5)插入数据慢 , 因为mysql采用的B+tree的结构来存储索引 , 假如是数据库自带的那种主键自增 , 节点满了 , 会裂变出新的节点 , 新节点满了 , 再去裂变新的节点 , 这样利用率和效率都很高 。 而UUID是无序的 , 会造成中间节点的分裂 , 也会造成不饱和的节点 , 插入的效率自然就比较低下了 。
三、Redis生成ID当使用数据库来生成ID性能不够要求的时候 , 我们可以尝试使用Redis来生成ID 。 这主要依赖于Redis是单线程的 , 所以也可以用生成全局唯一的ID 。 可以用Redis的原子操作 INCR和INCRBY来实现 。 可以使用Redis集群来获取更高的吞吐量 。 假如一个集群中有5台Redis 。 可以初始化每台Redis的值分别是1,2,3,4,5 , 然后步长都是5 。 各个Redis生成的ID为:
A:1,6,11,16,21B:2,7,12,17,22C:3,8,13,18,23D:4,9,14,19,24E:5,10,15,20,25这个 , 随便负载到哪个机确定好 , 未来很难做修改 。 但是3-5台服务器基本能够满足器上 , 都可以获得不同的ID 。 但是步长和初始值一定需要事先需要了 。 使用Redis集群也可以方式单点故障的问题 。 另外 , 比较适合使用Redis来生成每天从0开始的流水号 。 比如订单号=日期+当日自增长号 。 可以每天在Redis中生成一个Key , 使用INCR进行累加 。
优点:
1)不依赖于数据库 , 灵活方便 , 且性能优于数据库 。
2)数字ID天然排序 , 对分页或者需要排序的结果很有帮助 。
缺点:
1)如果系统中没有Redis , 还需要引入新的组件 , 增加系统复杂度 。
2)需要编码和配置的工作量比较大 。
四、雪花算法 (snowflake , Java版)SnowFlake算法生成id的结果是一个64bit大小的整数 , 它的结构如下图:
分布式雪花算法获取id文章插图

  • 1位 , 不用 。 二进制中最高位为1的都是负数 , 但是我们生成的id一般都使用整数 , 所以这个最高位固定是0
  • 41位 , 用来记录时间戳(毫秒) 。
    • 41位可以表示$2^{41}-1$个数字 ,
    • 如果只用来表示正整数(计算机中正数包含0) , 可以表示的数值范围是:0 至 $2^{41}-1$ , 减1是因为可表示的数值范围是从0开始算的 , 而不是1 。
    • 也就是说41位可以表示$2^{41}-1$个毫秒的值 , 转化成单位年则是$(2^{41}-1) / (1000 * 60 * 60 * 24 * 365) = 69$年
  • 10位 , 用来记录工作机器id 。
    • 可以部署在$2^{10} = 1024$个节点 , 包括5位datacenterId和5位workerId
    • 5位(bit)可以表示的最大正整数是$2^{5}-1 = 31$ , 即可以用0、1、2、3、....31这32个数字 , 来表示不同的datecenterId或workerId
  • 12位 , 序列号 , 用来记录同毫秒内产生的不同id 。
    • 12位(bit)可以表示的最大正整数是$2^{12}-1 = 4095$ , 即可以用0、1、2、3、....4094这4095个数字 , 来表示同一机器同一时间截(毫秒)内产生的4095个ID序号
由于在Java中64bit的整数是long类型 , 所以在Java中SnowFlake算法生成的id就是long(18位)来存储的 。
优点:
1)所有生成的id按时间趋势递增
2)整个分布式系统内不会产生重复id(因为有datacenterId和workerId来做区分)
3)不依赖于其他系统 , 可直接编写
缺点: