7. 分布式统一ID策略与Disruptor作用

1.业务ID生成方式与含义

使用带有业务含义的ID生成策略,这种方式也在传统应用系统、特定的场景下非常的好用。比如我们现在有一张商品货架表,这张表的数据维度是这样的,比如是按照城市和区域来划分的。

•比如北京我们按照100000为基本维度数据,100010为北京的一个区域,100020则为另一个区域,以此类推,200000可能是另一个城市,200010 则为另一个城市的区域。那么我们在生成货架信息ID的时候,可以按照前六位为城市和区域的方式进行组织,后面可以拼接一个简单的32位UUID字符串:

image.png
前8位都是具有业务含义的:前四位为城市维度编码,第五、六位为区域维度编码,最后两位为货架类型编码。

从前六位来看,这四条数据其中前三条数据属于一个城市下的数据信息,那么我们查询一个城市下的货架数据,查询语句可以为:

select * from goods_shelf gs
where id > '10001000000000000000000000000000000000'
  and id <'20000000000000000000000000000000000000'
image.png

如果想要查询1000(北京)这个城市下10区域的数据,则查询语句如下:

select * from goods_shelf gs
where id > '10001000000000000000000000000000000000'
 and id <'10002000000000000000000000000000000000'
image.png

那么我们如果想查询北京这个城市下面第一个区域的类型为01的货架集合,sql语句如下:

select * from goods_shelf gs
where id > '10001001000000000000000000000000000000000'
  and id < '10001002000000000000000000000000000000000'

image.png

2.Redis生成分布式统一ID问题

Redis缓存,利用Redis的分布式锁去解决此类问题.首先第一点,Redis本身也会有性能问题,在超高的并发下可能会有延迟等一些细节问题,第二点我们的服务调用链过长,我们的服务调用可能失败,因为期间过程是调用统一ID生成服务,然后再去Redis里set id,可能会出现重复情况,那么就需要进行重试。
延迟或重试可能会发生服务调用超时问题,导致我们的服务调用失败。


image.png

高并发下可能会指定核心业务超时时间 50ms内
而Redis服务重试时间可能 达到150ms+
直接就报超时了

3.业界主流分布式统一ID生成策略

实现一:提前加载,也就是预加载的机制

预加载机制,其实现核心如下:

  1. 提前加载,也就是预加载的机制
  2. 并发的获取,采用Disruptor框架提升性能
实现二:单点生成方式

单点生成方式,其核心实现如下:

  1. 固定的一个机器节点来生成一个唯一的ID,好处是能做到全局唯一
  2. 需要相应的业务规则拼接:机器码 + 时间戳 + 自增序列
    注意:但是要注意NTP服务器问题
为什么要进行拼接自增序列

在高并发的场景下,我们的生成ID请求量非常的巨大,就会暴露出NTP的问题。

NTP是网络时间协议(Network Time Protocol),它是用来同步网络中各个计算机的时间的协议
举个例子,如果单纯的采用:机器码+时间戳 或者 机器码+UUID这种组合,在高并发下可能会有订单重复的问题。


例:
机器码:ABK007
当前时间戳 1624520050000
ID生成为:ABK0071624520050000


NTP就是我们的服务器系统的时间会定时去获取,然后进行更新校准,也就是说,我们现在的系统时间是:1624520050001(ABK007)
生成了一个ID为:ABK0071624520050001的订单


紧接着我们的服务器进行了NTP的时间校准,这个时候系统时间肯定是向前走的,比如NTP在校准前的一刻,系统时间为:16245200512345(ABK007)
恰巧我们的服务器时间走的比较快,然后经过了一次NTP服务时间校准系统时间可能就会回到从前,回到过去
回到我们生成订单前的时间之前,比如1624520049999(ABK007)
高并发下后续的订单id生成的时间可能还会生成一个订单为:
ABK0071624520050001,这个时候我们就会产生订单id的重复问题。

使用:机器码 + 时间戳 + 自增序列,即时是我们的时间戳被NTP服务校准产生回调,那么我们自增系列永远会在单点下保证自增,比如AtomicLong就是很好的一种原子自增序列计数器

注意:实际生产环境中最好采取预加载的机制 + 单点生成方式(兜底)保持高可用
当预加载的机制不可用时,立即切换到单点生成方式
  1. 每一次预加载的生成Pre-MaxId (Pre-MaxId > db中 MaxId),存到Redis或者DB中
  2. Pre-MaxId为基础,结合单点生成方式的分布式Id也是唯一的。

4.Disruptor在分布式统一ID的使用

image.png

Disruptor负责生产和消费分布式id,提高性能

特别感谢:

阿神

你可能感兴趣的:(7. 分布式统一ID策略与Disruptor作用)