分布式系统全局发号器的几点思考
原文链接:何晓东 博客
文章起源于 康神交流群的 panda大佬和boss li关于发号器的一些交流,特此感谢让我们学到了新知识。
为什么需要发号器
在分布式系统中,经常需要对大量的数据、消息、http 请求等进行唯一标识,例如:对于分布式系统,服务间相互调用需要唯一标识,调用链路分析,日志追踪的时候需要使用这个唯一标识。此时需要一个全局唯一的 ID。
需要什么样子的发号器
持久化
要满足长期全局唯一,持久化是必须的,肯定不能让已经使用的再次产生一遍,同时需要强一致性。可用选择存储在 Redis 或者 Etcd 中。
高可用
这个时候需要提供发号器服务的机器主从同步,能够在主服务器宕机的时候,自动选择从服务器,切换过程中,发号器生成的 ID 可能不连续,服务正常就可以。
其他特性
主要是看具体业务了,需要认证和权限控制都是可选的,可用在请求层限制来源 IP,只允许固定的 IP 访问。
发号器的几种常用方案
UUID
UUID 是 Universally Unique Identifier 的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符,UUID 是16字节128位长的数字,通常以36字节的字符串表示,比如:3F2504E0-4F89-11D3-9A0C-0305E82C3301。
UUID经由一定的算法机器生成,为了保证 UUID 的唯一性,规范定义了包括网卡 MAC 地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成 UUID 的算法。UUID 的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。
优缺点: 本地生成,性能高,延迟低,位数长,不适用当作索引字段,同时是无序的,难以根据特征分析趋势。
类snowflake算法
snowflake是twitter开源的分布式ID生成算法,其核心思想为,一个long型的ID:
41bit作为毫秒数 - 10bit作为机器编号 - 12bit作为毫秒内序列号
算法单机每秒内理论上最多可以生成1000*(2^12),也就是400W的ID,
优缺点: 整个 ID 都是自增的,这个非常适合查看趋势,同时生成不依赖于第三方系统,可靠性很高,可调整性也很高。缺点就是严重依赖机器时钟。
基于MySQL的发号器
字段设置 auto_increment_increment
和 auto_increment_offset
来保证 ID 自增,每次业务使用下列 SQL 读写 MySQL 得到 ID。
begin; REPLACE INTO Tickets64 (stub) VALUES ('a'); SELECT LAST_INSERT_ID(); commit;
为了保证高可用,需要有多台 MySQL 机器,也需要为每台机器设置不同的自增起始值和步长,例如:
# TicketServer1: auto-increment-increment = 2 auto-increment-offset = 1 # TicketServer2: auto-increment-increment = 2 auto-increment-offset = 2
优缺点: 简单可靠,成本很小,由专业 DBA 进行维护就可以的。ID 单调递增,有合适的业务是最好的。缺点就是强依赖于数据库,修改起点和步长优点困难,同时也需要额外保证数据库的稳定可用。每次请求都去额外读写数据库的情况下,造成数据库压力很大,需要消耗很多资源。
以上就是最常用的三种全局唯一 ID 生成方案,采用较多的是类 snowflake 的方案,如果请求数列不是很高,可以调低时间的精度等,灵活性很高。生成的 ID 时间趋势自增,甚至可以用来做索引字段,占用空间较小。后期对数据进行分析的时候也很方便。
生产环境唯一ID的使用
类 snowflake 的方案,会采用请求时生成的方式,无法提前生成。
基于 MySQL 的发号器,或者 uuid 模式的,可用提前生成一大批数据,根据业务去定生成数量,放到内存,MQ 或者 Redis List中,业务申请唯一 ID 的时候,直接从其中弹一个出来。同时加一个异步定时任务,固定时间计算一下队列中剩余的唯一 ID 数量,数量不足时及时的再次生成一大批,加入到备选队列。
Go snowflake的demo:
参考文章:
一如既往,推荐几个 高质量课程,你学到新东西,我赚点佣金,大家都是赢家。