10分钟搞定!Golang分布式ID集合( 二 )


大名鼎鼎的雪花算法 , 这里不做过多介绍了 。 相对于UUID来说 , 雪花算法不会暴露MAC地址更安全、生成的ID也不会过于冗余 。 雪花的一部分ID序列是基于时间戳的 , 那么时钟回拨的问题就来了 。 上面提到的xid , 一定程度上避时钟回拨的影响 。 那么什么是时钟回拨 , 后面会提到 。数据库自增ID
这里常规是指数据库主键自增索引 。 特点如下:
架构简单容易实现;
ID有序递增 , IO写入连续性好;
INT和BIGINT类型占用空间较小;
由于有序递增 , 易暴露业务量;
受到数据库性能限制 , 对高并发场景不友好 。
bigint最大是2^64-1 , 但是数据库单表肯定放不了这么多 , 那么就涉及到分表 。 如果业务量真的太大了 , 主键的自增id涨到头了 , 会发生什么?报错:主键冲突 。Redis生成ID
通过redis的原子操作INCR和INCRBY获得id 。 相比数据库自增ID , redis性能更好、更加灵活 。 不过架构强依赖redis , redis在整个架构中会产生单点问题 。 在流量较大的场景下 , 网络耗时也可能成为瓶颈 。
ZooKeeper唯一ID
ZooKeeper是使用了Znode结构中的Zxid实现顺序增ID 。 Zookeeper类似一个文件系统 , 每个节点都有唯一路径名(Znode) , Zxid是个全局事务计数器 , 每个节点发生变化都会记录响应的版本(Zxid) , 这个版本号是全局唯一且顺序递增的 。 这种架构还是出现了ZooKeeper的单点问题 。
号段模式
Leaf-segment
把数据库自增主键换成了计数法 。 每个业务分配一个biz_tag、并记录各业务最大id(max_id)、号段跨度(step)等数据 。 这样每次取号只需要更新biz_tag对应的max_id , 就可以拿到step个id 。
10分钟搞定!Golang分布式ID集合
文章图片
优点
除了拥有自增ID的优点之外 , 在性能上比自增ID更好
扩展灵活 。
使用灵活、可配置性强 。
缓存机制 , 突发状况下短时间内能保证服务正常运转 。
缺点
id是有序自增 , 容易暴露信息 , 不可用于订单 。
在leaf的缓存ID用完再去获取新号段的间隙 , 性能会有波动 。
强依赖DB 。
增强版Leaf-segment
增强版是对上面描述的缺点2进行的改进——双cache 。 在leaf的ID消耗到一定百分比时 , 常驻的后台进程会预先去号段服务获取新的号段并缓存 。 具体消耗百分比、及号段step根据业务消耗速度来定 。
10分钟搞定!Golang分布式ID集合
文章图片
Tinyid
和增强版Leaf-segment类似 , 也是号段模式 , 提前加载号段 。
10分钟搞定!Golang分布式ID集合
文章图片
Leaf-snowflake
时钟回拨
服务器上的时间突然倒退回之前的时间 。 可能是人为的调整时间;也可能是服务器之间的时间校对 。
实现方案
10分钟搞定!Golang分布式ID集合
文章图片
10分钟搞定!Golang分布式ID集合】用Zookeeper顺序增、全局唯一的节点版本号 , 替换了原有的机器地址 。 解决了时钟回拨的问题 。 前面介绍ZooKeeper的缺点 , 强依赖ZooKeeper、大流量下的网络瓶颈 。 下图的方案在Leaf-snowflake中通过缓存一个ZooKeeper文件夹 , 提高可用性 。 运行时运行时 , 时差小于5ms会等待时差两倍时间 , 如果时差大于5ms报警并停止启动 。
10分钟搞定!Golang分布式ID集合
文章图片