sequence-分布式发号器

Posted by yueLng on 2018-03-13

简介

什么是分布式发号器,发号器有什么特点,有什么应用场景。在数据库中通常使用数据库自增主键来产生,但是如果不依赖数据库来产生全局唯一ID就需要另外想办法。例如在短网址服务的时候,短网址服务的作用是将一个长而复杂的url转为简短的url,同时短网址也可以转化为源网址。要实现这样的功能关键在如何将长网址与短网址映射,使用压缩或者md5算法是不可取的。简单而有效地做法就是每处理一个长网址就自增计数与长网址对应。利用mysql的主键自增的特性来构建发号器当然是可行的,也有公司采用了这种简单粗暴的方式,而且性能还不错(这里不考虑主备切换)

注意的点

  • 保证效率,发号服务应该是QPS很高的服务,所以要保证性能
  • 保证sequence递增正确,可以容忍错误,例如如果发号器崩溃,发号器不会重复发号
  • 不受外部环境影响,例如不受本地机器时间的影响,有的采用本地时间作为唯一id控制

主要设计

  • 持久化,持久化的方案也跟整个架构是分布式还是集中式相关,如果是分布式需要采用共识算法(例如多机NRW策略),集中式更加容易维护与理解,可以采用etcd作为存储,etcd自身包含一个集群保证,不会丢失数据。发号器可以保证无状态

  • 是否全局单调,这样就回到分布式的基本问题CAP问题,既然选择了高可用,那肯定就放弃了分布式,就只能采用主备方案,如何仲裁协调主备,也可以通过etcd服务注册与发现机制实现

  • ID形式,一种是sequence不断递增,另一种基于 Timestamp

微信序列号生成器

需求:递增的64位整型变量,每个用户都有自己独立的64位sequence空间

预分配中间层(解决磁盘I/O 问题)

  • 内存中储存最近一个分配出去的sequence:cur_seq,以及分配上限:max_seq
  • 分配sequence时,将cur_seq++,同时与分配上限max_seq比较:如果cur_seq > max_seq,将分配上限提升一个步长max_seq += step,并持久化max_seq
  • 重启时,读出持久化的max_seq,赋值给cur_seq

分号段共享存储(重启时要读取大量的max_seq数据加载到内存中,每个用户都有max_seq)

  • 通过uid相邻用户共享同一个max_seq,大幅减少max_seq需要存储的数据,降低IO次数

工程实现:

  • 把存储层和缓存中间层分成两个模块StoreSvr及AllocSvr。StoreSvr为存储层,利用了多机NRW策略来保证数据持久化后不丢失;AllocSvr则是缓存中间层,部署于多台机器,每台AllocSvr负责若干号段的sequence分配,分摊海量的sequence申请请求。

  • 整个系统又按uid范围进行分Set,每个Set都是一个完整的、独立的StoreSvr+AllocSvr子系统。分Set设计目的是为了做灾难隔离,一个Set出现故障只会影响该Set内的用户,而不会影响到其它用户。

容灾备份:主备容灾,嵌入式路由表容灾

参考资料

如何做一个靠谱的发号器
万亿级调用系统:微信序列号生成器架构设计及演变