统一ID生成服务

方案前提

ID获取的大前提至少做到单表有序

1、单机本地方案

架构图

原理:利用更新数据库字段方式实现 ID增长与持久化

实现:

  1、建表t_id_provider, 包含字段 max_id(当前最大ID值) upd_time(最后一次更新时间)

  2、IdService获取id集合使用本地锁,先从数据库取出最大值cMaxId = max_id,然后根据 update max_id = (max_id+取Id个数) where max_id = cMaxId (进行max_id对比主要是防止代码加锁BUG导致重复ID,作为最后一道保障)

优点:

  实现简单,代码量小

缺点:

  不支持高并发, 且单机模式可用性低,一旦挂了其他服务就无法获取到ID

2、集群本地方案

架构图

原理:工作原理和本地方案基本一致,区别是IdService获取ID集合由本地锁改为基于redis的分布式锁,集群服务用于保障高可用,以及提升不大的并发量(全局锁缘故)

3、单机本地缓存方案

架构图

原理:本地缓存,取一次用多次的方式,服务重启后未使用的ID弃用

实现:

  1、建表t_id_provider, 包含字段 max_id(当前最大ID值) upd_time(最后一次更新时间)

  2、IdService每次步长增加1万到10万不等,记录cId(当前ID) maxId(当前缓存最大可用ID)

  3、其他服务向IdService申请Id时对操作加锁,判断 cId+申请步长<=maxId,若小于等于则修改 cId=cId+申请步长,若不够则从数据库申请足够长度的步长(以万为单位),

  4、根据cId和步长大小,返回id集合

缓存法是对单机方式的优化,极大减少与数据库交互,并发数显著上升,但因单机模式并发任然不算太高

4、集群本地号段缓存方案

架构图

原理:号段预发本地缓存方式

实现:

  1、建表t_id_provider, 包含字段 max_id(当前最大ID值) upd_time(最后一次更新时间)

  2、IdService每次步长增加1万到10万不等,记录cId(当前ID) maxId(当前缓存最大可用ID)

  3、IdService启动时向mysql申请号段(redis分布式锁加锁), 例:IdService1 cId=1 maxId=10000;IdService2 cId=10001 maxId=20000; IdService3 cId=20001 maxId=30000;

  4、其他服务向IdService申请Id时使用redis分布式锁进行加锁,判断 cId+申请步长<=maxId,若小于等于则修改 cId=cId+申请步长,若不够则从数据库申请足够长度的步长(以万为单位),

关于拓展:若IdService服务需要横向拓展,增加号段提供

故障转移:

5、中间件REDIS方案(将运算交于REDIS)

架构图

原理:使用redis自带的 INCRBY计数命令,实现ID获取

实现:

  1、基于redis单线程、高性能的特点,IdService部署集群模式用于保障可用性

  2、提前 设置redis硬盘持久化, 设置 key id_generator 永不过期,初始化id_generator 值

  3、使用redis自带的计数器功能INCRBY命令实现key的步长增长,并返回增长后的值 returnNum

  4、根据 returnNum和步长生成id集合并返回

优点:

  基本满足高并发和高可用两点

缺点:

  各个服务的id生成依赖于一个key, 集中于同一台REDIS服务器上

改进

对id_generator 进行拆分,生成多个key,每个key保存一个号段,使用完或者不够用则向id_generator申请,将取id的业务分散在多台redis服务器上

例:hash("com.xxx.app.Test") = 100 再取余100%3=1,key为 id_generator_1,另有 id_generator_0和 id_generator_2,使用hash数据结构两个key分别是key_min key_max 作为区间使用

通过以上计算方式我们可以将原本的 id_generator 分散到了不同的机器上,此时id_generator用以维护当前最大值 而相应的号段则分配在 id_generator_0、id_generator_1、id_generator_2

当其他服务申请id集合时,通过计算得出key(id_generator_0、id_generator_1、id_generator_2),   执行lua脚本,脚本逻辑如下

a、判断  申请ID个数 <= key_max-key_min+1 是否成立, 若成立 则key_min+=申请ID个数   返回key_min 

b、若 申请ID个数 <= key_max-key_min+1 不成立,则 计算需要拓展值 num = 申请ID个数+1000 然后 对 id_generator INCRBY(num),key_max+=num, key_min+=申请ID个数  返回key_min

 

原文地址:https://www.cnblogs.com/xieyanke/p/13840727.html