引言
原文链接:https://yunsonbai.top/2020/01/21/snowflake_deploy/1
2
3ID在实际生产中起着非常重要的作用,任务追踪,任务查询等等等,尤其是在数据库层面,如果有一个id(非主键)能做唯一索引
那查询速度将变得快(常数级)的惊人,但是这种唯一索引最好使用数字,而且是增加的。很多人可能有疑问,为什么唯一索引要
建议使用int且一直增加的数字,建议大家查阅一下MySQL是怎么插入数据的。本文将介绍分布式ID生成算法snowflake的部署
关于snowflake
- 官方介绍: [文档](https://github.com/twitter-archive/snowflake
- 优势:
- 基于时间戳只能递增,并且能持续使用60多年(那会业务还有的话,肯定要优化)
- 分布式,几乎完全的水平扩展(1024个,实测go版单机(2C+4G)能到3万qps)
- 需要考虑的问题
- 怎么部署
- 有时间回滚怎么办
服务的依赖包
- snowflake: https://github.com/bwmarrin/snowflake
- web框架: https://github.com/gin-gonic/gin
接下来的名词说明
- 节点: 提供服务的服务器
- 节点ID: 雪花算法启动需要一个number, ID就是这个number
- node资源队列: 存放节点ID的队列
- 心跳: 定时上报节点信息的协程, 2秒一次
本服务数据结构
图示
说明(以上数据存在redis)
- node资源队列: 即将启动的node ID(1-1024), 存放单纯的数字
- nodeInfo表: hash表, 存放心跳信息。 key: id, value: jsonStr jsonStr: ‘{“t”: <时间戳>, “sid”: <上报节点机器唯一标识>}’
部署说明
节点id发放
流程图
说明
- 投放的总节点数n不能超过1024
- n依次递减得到ID, 如果在信息表存在该ID则跳过,投放其他ID
- 如需减少实例,可直接杀死实例,实例会回收ID到资源队列,等待下次使用
节点的启动/恢复
流程图
流程图说明
- 尝试从node资源队列里获取ID, 最多重试3次, 每次间隔1秒, 若取到返回ID
若1失败, 尝试从nodeInfo表里获取20秒以上没有心跳的节点ID, 最多重试3次,每次间隔5秒, 若取到返回ID
- 20秒的意义: 避免时间回滚(一般集群定时同步时间, 若出现出现20秒以上回滚, 问题有点大)
- 重试的意义: 尽可能拾起死掉的ID
如果1和2都没拿到ID, 服务终止。否则启动节点服务并尝试发心跳
- 当满足以下情况不能发起心跳, 且终止服务, 并销毁该ID
- nodeInfo有在该ID的心跳, 上报节点不是自己且最新上报时间距离现在<20秒
关键点说明
- ID的回收: 服务终止时, 由退出信号监听协程负责把ID重新放回node资源队列,如果ID为0, 则不再放回(所谓的销毁)。
- ID销毁: 把本节点拿到的ID置0
节点增加/更新
增加:
- 确定目前node资源队列和nodeInfo表中有的ID
- 假定增加后节点数为N, 目前所有的节点数为n。若N>n, 需要重新投放, 投放总数为N(注意看前边的投放规则), 若N<=n, 不用投放直接启动剩余节点
更新: 采取先终止服务(终止时节点会回收ID), 在启动新节点。可以一个或多个操作,注意不要全部杀死, 否则会影响线上服务。
结束语
1 | 分布式ID生成算法snowflake部署的最大难点在于同一时刻不能存在两个一样的node ID |