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生成策略
实现一:提前加载,也就是预加载的机制
预加载机制,其实现核心如下:
- 提前加载,也就是预加载的机制
- 并发的获取,采用Disruptor框架提升性能
实现二:单点生成方式
单点生成方式,其核心实现如下:
- 固定的一个机器节点来生成一个唯一的ID,好处是能做到全局唯一
- 需要相应的业务规则拼接:机器码 + 时间戳 + 自增序列
注意:但是要注意NTP服务器问题
为什么要进行拼接自增序列
在高并发的场景下,我们的生成ID请求量非常的巨大,就会暴露出NTP的问题。
NTP是网络时间协议(Network Time Protocol),它是用来同步网络中各个计算机的时间的协议
举个例子,如果单纯的采用:机器码+时间戳 或者 机器码+UUID这种组合,在高并发下可能会有订单重复的问题。
例:
机器码:ABK007
当前时间戳 1624520050000
ID生成为:ABK007
1624520050000
NTP就是我们的服务器系统的时间会定时去获取,然后进行更新校准,也就是说,我们现在的系统时间是:1624520050001(ABK007)
生成了一个ID为:ABK007
1624520050001的订单
紧接着我们的服务器进行了NTP的时间校准,这个时候系统时间肯定是向前走的,比如NTP在校准前的一刻,系统时间为:16245200512345(ABK007)
恰巧我们的服务器时间走的比较快,然后经过了一次NTP服务时间校准系统时间可能就会回到从前,回到过去
回到我们生成订单前的时间之前,比如1624520049999(ABK007)
高并发下后续的订单id生成的时间可能还会生成一个订单为:
ABK007
1624520050001,这个时候我们就会产生订单id的重复问题。
使用:机器码 + 时间戳 + 自增序列,即时是我们的时间戳被NTP服务校准产生回调,那么我们自增系列永远会在单点下保证自增,比如AtomicLong
就是很好的一种原子自增序列计数器
注意:实际生产环境中最好采取预加载的机制 + 单点生成方式(兜底)保持高可用
当预加载的机制不可用时,立即切换到单点生成方式
- 每一次预加载的生成
Pre-MaxId
(Pre-MaxId
> db中MaxId
),存到Redis或者DB中 - 以
Pre-MaxId
为基础,结合单点生成方式的分布式Id
也是唯一的。
4.Disruptor在分布式统一ID的使用
image.pngDisruptor负责生产和消费分布式id,提高性能
网友评论