分布式系统中生成全局唯一ID的方法

阅读了这篇文章而后总结了下我们系统中全局唯一ID的生成方法。
http://www.cnblogs.com/heyuquan/archive/2013/08/16/global-guid-identity-maxId.html

主键ID采用UUID,只作为唯一性标识。
部分业务表需要有业务No的,我们采用以下方法生成唯一ID:
从redis中用lpop指令取指定key值的数据。(lpop:移除并返回列表的头元素)
如果将指定key值的数据取完了,会触发初始化。
初次初始化:
1)用for update锁表,存储最小值1和最大值50到数据库中。
2)将这50个数字放入redis中。
下次初始化:
1)用for update锁表,存储最小值51和最大值100到数据库中。
2)将这50个数字放入redis中。
将返回的值组装成全局唯一性ID。

数据库有脚本每天清理表数据,避免最大值与最小值过大。
这里的指定key是根据业务编号+日期组成的,确保每天不同。
附代码:

// 设置no
String applyNo = cityCodeDto.getCityCode() + DateUtils.getCurrentTimeNum();
applyReceptionDto.setApplyLoanNo(generatorService.getNextVal(applyNo, 5).getData());
    @Override
    public ServiceResult<String> getNextVal(String sequenceName, Integer bit) {
        Long nextValue = getNextValFromRedis(sequenceName);
        if (nextValue != null) {
            return ServiceResult.newSuccess(formatBitValue(sequenceName, nextValue, bit));
        }
        MyphLogger.debug("getNextVal start init sequenceName ,sequenceName=", sequenceName);
        initSequence(sequenceName);
        MyphLogger.debug("getNextVal end init sequenceName,sequenceName=" + sequenceName);
        //重新尝试从redis获取
        Long id = getNextValFromRedis(sequenceName);

        if (id == null) {
            return ServiceResult.newFailure("id生成器非预期异常");
        }

        return ServiceResult.newSuccess(formatBitValue(sequenceName, id, bit));
    }
    private Long getNextValFromRedis(String sequenceName) {
        Long nextVal = CacheService.ListKey.lpop(SequenceHelper.getKey(sequenceName), Long.class);

        MyphLogger.debug(sequenceName + "value" + nextVal);

        if (nextVal == null) {
            return null;
        }

        try {
            return nextVal;
        } catch (Exception e) {
            throw new RuntimeException("seqkey放入非法字符,key=" + SequenceHelper.getKey(sequenceName));
        }

    }
    /**
     * 执行redis数据队列的预生成流程,生成批量一批数据到redis队列供服务使用
     *
     * @param sequenceName
     */
    private void initSequence(String sequenceName) {
        String minValue = SequenceHelper.getSeqMinValue(sequenceName);
        generatorService.addSeqToCacheByName(sequenceName, minValue);
    }
@Service
public class GeneratorServiceImpl implements GeneratorService {
    private static final Long startNo = 1l;
    private static final Integer initialCapacity = 50;
    @Resource
    private MyphSequenceDao myphSequenceDao;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void addSeqToCacheByName(String seqName, String minValue) {

        // 使用for update 锁住记录 防止并发
        MyphSequence seq = myphSequenceDao.queryAndLockSeq(seqName);
        if (null == seq) {
            MyphSequence sequence = new MyphSequence();
            sequence.setMinValue(startNo);
            sequence.setEnable(true);
            sequence.setInitialCapacity(initialCapacity);
            sequence.setSequenceName(seqName);
            myphSequenceDao.add(sequence);

            seq = myphSequenceDao.queryAndLockSeq(seqName);
        }
        String nowMinValue = SequenceHelper.getSeqMinValue(seqName);

        MyphLogger.debug("IdGeneratorService1 " + minValue + " " + nowMinValue + "");
        // 如果nowMinValue为null,可以执行初始化
        // 如果 nowMinValue 和 minValue 不等,说明之前有人改动过这个值,此次不需要操作了
        if (StringUtils.isNotEmpty(nowMinValue) && !StringUtils.equals(nowMinValue, minValue)) {
            MyphLogger.debug("IdGeneratorService :normal return :nowMinValue=" + nowMinValue + ",preminValue=" + minValue + ",seqname=" + seqName);
            return;
        }

        // 查询结果入redis 全部结果一次插入
        long currentMinValue = seq.getMinValue();
        long nextMinValue = currentMinValue + seq.getInitialCapacity() * 1;
        long maxValue = (seq.getMaxValue() == null) ? Long.MAX_VALUE : seq
                .getMaxValue();
        nextMinValue = nextMinValue > maxValue ? maxValue : nextMinValue;

        MyphLogger.debug("IdGeneratorService addSeqToCache start1--:seq[name=" + seq.getSequenceName() + ",min=" + seq.getMinValue() + ",max=" + seq.getMaxValue() + ".nextMin=" + nextMinValue + ",updateTime=" + seq.getUpdateTime());

        seq.setMinValue(nextMinValue);

        myphSequenceDao.updateSeqInfo(seq.getId(), seq.getMinValue());

        MyphLogger.debug("IdGeneratorService addSeqToCache start2--: update db minavlue:nextMinValue=" + nextMinValue + ",currentMinValue=" + currentMinValue + ",seqname=" + seqName);

        //redis set min value
        SequenceHelper.setSeqMinValue(seqName, String.valueOf(nextMinValue));

        List<Long> values = new ArrayList<Long>();
        for (long i = currentMinValue; i < nextMinValue; i += 1) {
            values.add(i);
        }
        CacheService.ListKey.rpush(SequenceHelper.getKey(seq.getSequenceName()), values);
    }

猜你喜欢

转载自blog.csdn.net/wee616/article/details/78710657