贵金属行情
微信公众号

snowflake分布式id部署

引言

原文链接:https://yunsonbai.top/2020/01/21/snowflake_deploy/

1
2
3
ID在实际生产中起着非常重要的作用,任务追踪,任务查询等等等,尤其是在数据库层面,如果有一个id(非主键)能做唯一索引
那查询速度将变得快(常数级)的惊人,但是这种唯一索引最好使用数字,而且是增加的。很多人可能有疑问,为什么唯一索引要
建议使用int且一直增加的数字,建议大家查阅一下MySQL是怎么插入数据的。本文将介绍分布式ID生成算法snowflake的部署

关于snowflake

  • 官方介绍: [文档](https://github.com/twitter-archive/snowflake
  • 优势:
    • 基于时间戳只能递增,并且能持续使用60多年(那会业务还有的话,肯定要优化)
    • 分布式,几乎完全的水平扩展(1024个,实测go版单机(2C+4G)能到3万qps)
  • 需要考虑的问题
    • 怎么部署
    • 有时间回滚怎么办

服务的依赖包

接下来的名词说明

  • 节点: 提供服务的服务器
  • 节点ID: 雪花算法启动需要一个number, ID就是这个number
  • node资源队列: 存放节点ID的队列
  • 心跳: 定时上报节点信息的协程, 2秒一次

本服务数据结构

  • 图示
    图1

  • 说明(以上数据存在redis)

    • node资源队列: 即将启动的node ID(1-1024), 存放单纯的数字
    • nodeInfo表: hash表, 存放心跳信息。 key: id, value: jsonStr jsonStr: ‘{“t”: <时间戳>, “sid”: <上报节点机器唯一标识>}’

部署说明

节点id发放

  • 流程图
    流程图1

  • 说明

    • 投放的总节点数n不能超过1024
    • n依次递减得到ID, 如果在信息表存在该ID则跳过,投放其他ID
    • 如需减少实例,可直接杀死实例,实例会回收ID到资源队列,等待下次使用

节点的启动/恢复

  • 流程图
    流程图2

  • 流程图说明

    1. 尝试从node资源队列里获取ID, 最多重试3次, 每次间隔1秒, 若取到返回ID
    2. 若1失败, 尝试从nodeInfo表里获取20秒以上没有心跳的节点ID, 最多重试3次,每次间隔5秒, 若取到返回ID

      • 20秒的意义: 避免时间回滚(一般集群定时同步时间, 若出现出现20秒以上回滚, 问题有点大)
      • 重试的意义: 尽可能拾起死掉的ID
    3. 如果1和2都没拿到ID, 服务终止。否则启动节点服务并尝试发心跳

    4. 当满足以下情况不能发起心跳, 且终止服务, 并销毁该ID
      • nodeInfo有在该ID的心跳, 上报节点不是自己且最新上报时间距离现在<20秒
  • 关键点说明

    • ID的回收: 服务终止时, 由退出信号监听协程负责把ID重新放回node资源队列,如果ID为0, 则不再放回(所谓的销毁)。
    • ID销毁: 把本节点拿到的ID置0

节点增加/更新

  • 增加:

    1. 确定目前node资源队列和nodeInfo表中有的ID
    2. 假定增加后节点数为N, 目前所有的节点数为n。若N>n, 需要重新投放, 投放总数为N(注意看前边的投放规则), 若N<=n, 不用投放直接启动剩余节点
  • 更新: 采取先终止服务(终止时节点会回收ID), 在启动新节点。可以一个或多个操作,注意不要全部杀死, 否则会影响线上服务。

结束语

1
2
3
4
5
6
7
分布式ID生成算法snowflake部署的最大难点在于同一时刻不能存在两个一样的node ID
运行和如何避免时间回滚, 本文几个解决方法:
退出监控协程, 为了回收ID方便下次使用尤其是在节点更新
心跳协程, 及时上报节点信息, 快速销毁ID一样的节点
服务恢复规则, 根据node资源队列(优先考虑)和nodeInfo表快速拾起死去的ID
两个辅助协程加一个恢复规则, 快速帮助系统恢复正常状态。
欢迎大家批评指正。
yunsonbai wechat
微信公众号