Leaf分布式ID

文章目录

系统对Id号的要求

1、业务

1)全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求

2)趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能

3)单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求

  • 数据自增id

4)信息安全:如果ID是连续的, 竞对在两天中午12点分别下单,通过订单id号相减就能大致计算出公司一天的订单量 。所以在一些应用场景下,会需要ID无规则

  • UUID或雪花算法

2、可靠性

  • 平均延迟和TP999延迟都要尽可能低
  • 可用性5个9
  • 高QPS

UUID

1、定义

36个字符,示例:550e8400-e29b-41d4-a716-446655440000

java 复制代码
public class IdUtil {
	/*
	 * 返回使用ThreadLocalRandom的UUID,比默认的UUID性能更优
	 */
	public static UUID fastUUID() {
		ThreadLocalRandom random = ThreadLocalRandom.current();
		return new UUID(random.nextLong(), random.nextLong());
	}
}

2、缺点

  • 太长,不易于存储
  • 无序性,如果作为数据库主键,可能会引起数据页位置频繁变动,严重影响性能
  • 信息不安全,基于MAC地址生成UUID的算法可能会造成MAC地址泄露

snowflake

1、结构

Long型64位的整数,如图1所示

  • 41-bit的时间戳

    可以表示(1L<<41)/(1000L360024*365)=69年的时间

  • 10-bit 数据中心ID和机器

    5bit数据中心id,5bit机器id,一般使用ZK分配

  • 12个自增序列号

    在同一毫秒内生成2^12个唯一的ID

理论上snowflake方案的QPS约为409.6w/s,可以保证在任何一个IDC、任何一台机器、在任意毫秒内、生成的ID都是不同的

2、问题

41-bit时间戳部分,强依赖机器时钟,如果机器上时钟回拨,会导致发号重复

Leaf

使用参考:Leaf生成单据号

Leaf-snowflake

1、适合场景:生成的ID需要无规则

2、解决机器时钟回拨问题

1)要求当前时间戳,必须 > 机器创建时间

2)同时

  • 对比其余Leaf节点的系统时间,来判断自身系统时间是否准确
  • RPC请求得到所有节点的系统时间,计算sum(time)/nodeSize < 阈值,则认为正确

阈值 = 5ms

因为理论上5ms内无法,完全使用完成后12个自增序列号,所以不会重复

否则直接报错自动摘除本身节点并报警

Leaf-segment

1、适合场景:生成的ID单调递增

2、实现:基于MySQL的自增主键

MySQL自增主键

1、获取ID方式

使用下列SQL读写MySQL得到ID号

复制代码
begin;
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
commit;
  • Tickets64:表
  • stub:列

实现方式类似:

复制代码
useGeneratedKeys="true" keyProperty="id"
  • int insert(XxxDO xxxDo)时,先将DO内容写入db
  • insert成功后,再将JDBC自增主键值AUTO_INCREMENT,回写到DO的id属性字段
  • 后续可能会从DO中获取此id值进行查询数据、编辑数据

2、存在问题

因为每次都是都需要写,读MySQL才能获取ID值,单台MySQL的读写性能是瓶颈

3、解决-集群

  • 在分布式系统中多部署几台机器

  • 每台机器设置不同的初始值,且步长和机器数相等

    比如有两台机器。设置步长step为2,TicketServer1的初始值为1(1,3,5,7,9,11...)

    TicketServer2的初始值为2(2,4,6,8,10...)

  • 假设部署N台机器,步长需设置为N,每台的初始值依次为0,1,2...N-1 ,则整个Leaf架构如图2

4、存在问题

  • 数据库压力还是很大,每次获取ID都得读写一次数据库,只能靠堆机器来提高性能
  • 系统水平扩展比较困难,定义好了步长和机器台数之后,如果要添加机器不好做

5、解决- 批量分段(segment)获取


segment

1、实现,如图3所示

1)db表设计

sql 复制代码
+-------------+--------------+------+-----+-------------------+-------------------------
| Field       | Type         | Null | Key | Default           | Extra                     
+-------------+--------------+------+-----+-------------------+--------------------------
| biz_tag     | varchar(128) | NO   | PRI |                   |                           
| max_id      | bigint(20)   | NO   |     | 1                 |                           
| step        | int(11)      | NO   |     | NULL              |                           
| desc        | varchar(256) | YES  |     | NULL              |                           
| update_time | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update 
  • biz_tag用来区分业务(外卖、支付)
  • max_id表示该biz_tag目前所被分配的ID号段的最大值
sql 复制代码
UPDATE table SET max_id=max_id+step WHERE biz_tag=xxx
  • step表示每次分配的号段长度

2)系统架构

3) ID值趋势递增

eg:test_tag业务

  • Leaf Server 1:从DB加载号段[1,1000]。
  • Leaf Server 2:从DB加载号段[1001,2000]。
  • Leaf Server 3:从DB加载号段[2001,3000]。

用户通过Round-robin的方式调用Leaf Server的各个服务,通过CAS获取ID,所以某一个Client获取到的ID序列

可能是:1,1001,2001,2,1002,2002......

也可能是:1,2,1001,2001,2002,2003,3,4...

当某个Leaf Server号段用完之后,下一次请求就会从DB中加载新的号段,这样保证了每次加载的号段是递增

2、解决-读写性能

  • 原来获取一个ID值,都需要读写一次数据库

  • 现在只需要把step设置得足够大,比如1000。那么只有当1000个号被消耗完了之后才会去重新读写一次

    读写数据库的频率从1减小到了1/step

  • test_tag业务,在第一台Leaf机器上是1~1000的号段,当这个号段用完时

  • 会去加载另一个长度为step=1000的号段

  • 假设另外两台号段都没有更新,这个时候第一台机器新加载的号段就应该是3001~4000

  • 同时数据库对应的biz_tag = test_tag 这条数据的max_id会从3000被更新成4000

3、 解决-扩容操作

只需要对biz_tag分库分表

4、问题

  • 在号段消耗完的时候进行取号段时,还是会夯在更新数据库的I/O上

  • 假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢

5、解决-双buffer


双buffer

1、解决-双buffer

  • 当其中一个Buffer中的号段消费到某个点(90%)时,就启异步线程的把下一个号段加载到内存中的另一个Buffer

2、 容灾

分库分表

4、问题

  • 在号段消耗完的时候进行取号段时,还是会夯在更新数据库的I/O上

  • 假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢

相关推荐
creator_Li3 天前
雪花算法Snowflake
雪花算法
袁袁袁袁满15 天前
Python使用uuid生成唯一密钥uid详细教程
开发语言·python·uuid·唯一密钥uid
CodeAmaz19 天前
分布式 ID 方案(详细版)
分布式·分布式id
小楼v20 天前
深入全面理解幂等性设计原理及实现幂等的主流方案
后端·雪花算法·幂等性·幂等设计
Zongsoft21 天前
自适应可变速率ID生成器的设计与实践(视频)
redis·uuid·分布式id·snowflake·sequence
CodeAmaz1 个月前
分布式ID原理与使用详解
雪花算法·分布式id·数据库号段
源代码•宸1 个月前
goframe框架签到系统项目开发(分布式 ID 生成器、雪花算法、抽离业务逻辑到service层)
经验分享·分布式·mysql·算法·golang·雪花算法·goframe
StarRocks_labs1 个月前
Fresha 的实时分析进化:从 Postgres 和 Snowflake 走向 StarRocks
数据库·starrocks·postgres·snowflake·fresha
0xAaron2 个月前
确定crash文件和dSYM是否对应
ios·uuid·crash·dsym
0xAaron2 个月前
符号表和 dSYM UUID 确认
ios·cocoa·uuid·符号表·dsym