如何设计一款“高可用高性能”的发号器

捉虫大师
• 阅读 1756

本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。

背景

在分布式场景中,很多地方需要生成全局唯一的id,如数据库分库分表后需要用唯一id代替单机版本的自增id。发号器的基本要求是

  • 全局唯一,无论如何都不能重复

某些场景下还要求单调递增,如排序需求等。

网上有很多介绍发号器的文章,比如美团的《Leaf——美团点评分布式ID生成系统》,有赞的《如何做一个靠谱的发号器》等。本文聚焦高可用,高性能

  • 高可用:不会因为系统故障导致服务不可用或发号重复
  • 高性能:发号器通常是一个非常高并发的系统,性能足够的同时也可以水平扩容 在基本的要求下,常见的解决方案有哪些?他们是否是高可用,高性能的呢?

snowflake方案

snowflake 采用41位时间戳加10位机器id加12位序列号的方式生成,序列号在单一进程内可使用AtomicLong来生成,10位机器号可支持1024台机器

如何设计一款“高可用高性能”的发号器

该算法优点是:

  • 算法简单,易于实现,不依赖任何第三方系统,性能非常高;
  • 集群无状态,可随意扩缩容,可认为是高可用系统。

缺点是:

  • 高10位的时间戳和低位自增序列号可保证单调增,但机器号无法保证,如机器号为2在某一时刻先生成id,机器号为1在同一时刻后生成id,则不能保证单调性;
  • 依赖时间戳,如果时钟回拨,可能会生成重复的id。

综合来看,snowflake方案符合基本要求,性能非常高,但其存在时钟回拨问题,因而是高性能不是高可用的方案。

基于数据库方案

利用数据库的自增id特性实现,该方案优点:

实现比较简单,只依赖数据库;

  • 没有时钟回拨问题;
  • 生成的id单调递增。

缺点:

  • 性能被数据库限制,数据库的单机写入性能有限,也无法扩缩容;
  • 数据库存在单点故障,如果是主从架构,取决于是异步复制、半同步复制、全同步复制配置,只有全同步复制才能保证可用性,其他配置无法保证主从数据的一致性,一旦主库发生故障,主库的变更还未应用到从库,则主从切换后可能会存在发号重复的问题。

同理,这里的数据库也可以替换为redis,利用redis的incr来实现,但redis只有异步复制,更加无法保证数据一致性。

综合来看,基于数据库的方案如果不开启全同步复制,就不是高可用方案,如果开启全同步复制,则性能一定会有问题(就算不开启全同步复制也会有性能问题)。

基于数据库的号段方案

本方案是对数据库方案的一种性能优化,每次从数据库取回的不是一个id,而是一个号段,在单独进程内通过锁保证每次发放一个唯一的id,甚至可以在系统快要发放完号码时异步地去获取下一个号段,该方案性能明显高于数据库方案,但也失去了id单调递增的特性,如果开启全同步复制,则可认为是一个高可用的方案,通过调大号段的长度,可以达到高性能的要求。

基于多主库的数据库方案

本方案也是对数据库号方案的一种优化,采用多台数据库,假设3台主库设置自增id起始分别为1,2,3,步长都设置为3,这样1号数据库获取的自增id为1,4,7...,2号数据库获取的自增id为2,5,8...,3号数据库获取的自增id为3,6,9...,他们永远不会重复。系统每次取号段时采取轮询策略,如果有一台数据库获取失败,则继续从下一个数据库获取。该方案解决了数据库的高可用问题,个别数据库宕机不影响系统正常运行。高性能也是通过号段的方式来解决,如果运行过程中对数据库进行水平扩容则比较困难。

基于一致性协议的方案

上面数据库的高可用问题主要来源于主从数据不一致,如果使用一致性协议来保证数据的一致性,就可以解决高可用问题,目前最常使用的raft算法,可以保证数据复制到半数以上机器。在我们每获取一个号段后,已发出的号段都被持久化到半数以上机器,并且最终复制到所有机器,当master挂掉后raft重新选举。有赞的《如何做一个靠谱的发号器》就是采取这种办法,他们使用的组件是etcd。甚至可以基于开源的raft库来自己实现一个发号器,如果要自己来实现一个靠谱的raft协议,还是比较困难的,开源的raft库可选用蚂蚁开源的SOFAJRaft。

总结

  • 发号器的高性能主要依靠号段的方式来解决;
  • 发号器的高可用可以依靠数据库的高可用、多主库、一致性协议来实现。

搜索关注微信公众号"捉虫大师",后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。

如何设计一款“高可用高性能”的发号器

点赞
收藏
评论区
推荐文章
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Nginx + lua +[memcached,redis]
精品案例1、Nginxluamemcached,redis实现网站灰度发布2、分库分表/基于Leaf组件实现的全球唯一ID(非UUID)3、Redis独立数据监控,实现订单超时操作/MQ死信操作SelectPollEpollReactor模型4、分布式任务调试Quartz应用
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Leaf:美团分布式ID生成服务开源
Leaf是美团基础研发平台推出的一个分布式ID生成服务,名字取自德国哲学家、数学家莱布尼茨的一句话:“Therearenotwoidenticalleavesintheworld.”Leaf具备高可靠、低延迟、全局唯一等特点。目前已经广泛应用于美团金融、美团外卖、美团酒旅等多个部门。具体的技术细节,可参考此前美团技术博客的一篇文章:《Lea
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Vitess全局唯一ID生成的实现方案 | 京东云技术团队
为了标识一段数据,通常我们会为其指定一个唯一id,比如利用MySQL数据库中的自增主键。但是当数据量非常大时,仅靠数据库的自增主键是远远不够的,并且对于分布式数据库只依赖MySQL的自增id无法满足全局唯一的需求。因此,产生了多种解决方案,如UUID,Sn