号段模式生成分布式ID

后端 潘老师 3个月前 (12-11) 81 ℃ (0) 扫码查看

本文主要讲解关于号段模式生成分布式ID 相关内容,让我们来一起学习下吧!

采用数据库号段模式来生成分布式id,包括生成有序id和无序id。

生成有序id

  1. 数据库中定义相关id生成策略的表,其中每一条记录代表一种id生成策略,包括id段的起始值、结束值、步长、版本号(乐观锁实现)。
  2. id生成服务启动时,先去数据库中获取指定id对应的id生成策略,抢占号段(更新数据库对应记录的起始值、结束值、版本号)。如果更新失败,则进行有限次数的重试(重新从数据库中获取指定id对应的id生成策略,重新尝试更新)。如果更新成功,则将对应号段加载到本地。为每一种生成策略绑定一个信号量,new Semaphore(1)。
  3. 本地采用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;
  1. 当调用获取id的方法时,传入对应的生成策略的id,根据id去本地内存中获取对应的号段对象,如果获取对象成功并且生成的id在号段范围内,则通过原子类的incrementAndGet方法获取生成的id。同时启动异步任务,检查更新本地的id号段。
  2. 在异步检查更新本地的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

  1. 本地存放无序id。 Map<Integer, LocalUnSeqIdBO> localUnSeqIdBOMap = new ConcurrentHashMap<>();
  2. 无序id对象属性,使用ConcurrentLinkedQueue提前存放无序id。
private int id;
/**
 * 提前将无序的id存放在这条队列中
 */
private ConcurrentLinkedQueue<Long> idQueue;
/**
 * 当前id段的开始值
 */
private Long currentStart;
/**
 * 当前id段的结束值
 */
private Long nextThreshold;
  1. 获取本地的无序id,Long returnId = localUnSeqIdBO.getIdQueue().poll();
  2. 在获取本地无序id时,如果已经使用的id达到阈值,尝试异步更新同步本地的id号段。
  3. 更新本地id号段的操作中,同样使用信号量控制进入的线程数为1。尝试去抢占数据库中的id段,获取到对应的id段后,将段内的id提前生成并打乱顺序放入队列中。

以上就是关于号段模式生成分布式ID 相关的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/12474.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】