文
章
目
录
章
目
录
本文主要讲解关于号段模式生成分布式ID 相关内容,让我们来一起学习下吧!
采用数据库号段模式来生成分布式id,包括生成有序id和无序id。
生成有序id
- 数据库中定义相关id生成策略的表,其中每一条记录代表一种id生成策略,包括id段的起始值、结束值、步长、版本号(乐观锁实现)。
- id生成服务启动时,先去数据库中获取指定id对应的id生成策略,抢占号段(更新数据库对应记录的起始值、结束值、版本号)。如果更新失败,则进行有限次数的重试(重新从数据库中获取指定id对应的id生成策略,重新尝试更新)。如果更新成功,则将对应号段加载到本地。为每一种生成策略绑定一个信号量,new Semaphore(1)。
- 本地采用ConcurrentHashMap来保存号段,形式为<id, 号段对象>。其中本地保存的当前id值为原子类,保证线程安全。
private static Map<Integer, LocalUnSeqIdBO> localSeqIdBOMap = new ConcurrentHashMap<>();
// 号段对象的属性
/**
* id生成策略,对应数据库中记录的主键
*/
private int id;
/**
* 在内存中记录的当前有序id的值
*/
private AtomicLong currentNum;
/**
* 当前id段的开始值
*/
private Long currentStart;
/**
* 当前id段的结束值
*/
private Long nextThreshold;
- 当调用获取id的方法时,传入对应的生成策略的id,根据id去本地内存中获取对应的号段对象,如果获取对象成功并且生成的id在号段范围内,则通过原子类的incrementAndGet方法获取生成的id。同时启动异步任务,检查更新本地的id号段。
- 在异步检查更新本地的id号段中,如果已经使用的id超过阈值,另起一个线程去抢占数据库中的号段,并加载到本地内存中。为了保证线程安全,使用信号量来控制进入同步更新本地号段的线程数为1。
private void refreshLocalSeqId(LocalSeqIdBO localSeqIdBO) {
long step = localSeqIdBO.getNextThreshold() - localSeqIdBO.getCurrentStart();
if (localSeqIdBO.getCurrentNum().get() - localSeqIdBO.getCurrentStart() > step * UPDATE_RATE) {
Semaphore semaphore = semaphoreMap.get(localSeqIdBO.getId());
if (semaphore == null) {
LOGGER.error("semaphore is null,id is {}", localSeqIdBO.getId());
return;
}
boolean acquireStatus = semaphore.tryAcquire();
if (acquireStatus) {
LOGGER.info("开始尝试进行本地id段的同步操作");
// 异步进行同步id段操作
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
try {
IdGeneratePO idGeneratePO = idGenerateMapper.selectById(localSeqIdBO.getId());
tryUpdateMySQLRecord(idGeneratePO);
} catch (Exception e) {
LOGGER.error("[refreshLocalSeqId] error is ", e);
} finally {
semaphoreMap.get(localSeqIdBO.getId()).release();
LOGGER.info("本地有序id段同步完成,id is {}", localSeqIdBO.getId());
}
}
});
}
}
}
生成无序id
- 本地存放无序id。 Map<Integer, LocalUnSeqIdBO> localUnSeqIdBOMap = new ConcurrentHashMap<>();
- 无序id对象属性,使用ConcurrentLinkedQueue提前存放无序id。
private int id;
/**
* 提前将无序的id存放在这条队列中
*/
private ConcurrentLinkedQueue<Long> idQueue;
/**
* 当前id段的开始值
*/
private Long currentStart;
/**
* 当前id段的结束值
*/
private Long nextThreshold;
- 获取本地的无序id,Long returnId = localUnSeqIdBO.getIdQueue().poll();
- 在获取本地无序id时,如果已经使用的id达到阈值,尝试异步更新同步本地的id号段。
- 更新本地id号段的操作中,同样使用信号量控制进入的线程数为1。尝试去抢占数据库中的id段,获取到对应的id段后,将段内的id提前生成并打乱顺序放入队列中。
以上就是关于号段模式生成分布式ID 相关的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!