全局唯一id

目录

唯一ID应该具备的要素

基于Redis的INCR命令实现

Redis命令:

配置文件 

工具类实现


 

唯一ID应该具备的要素

  • 唯一性: 确保生成的 ID 是全局唯一的
  • 有序递增性: 确保生成的 D 是对于某个用户或者业务是按一定的数字有序递增的
  • 高可用性: 确保任何时候都能正确的生成ID3)
  • 包含时间: 包含时间(年月日),一眼扫过去就知道哪天的交易

基于Redis的INCR命令实现

  • Redis的INCR命令具备了“INC AND GET”的原子操作,即增加并返回结果的原子操作。这个原子性很方便我们实现获取ID
  • Redis是单进程单线程架构,INCR命令不会出现id重复

基于以上2个特性,采用INCR命令来实现分布式全局ID生成。

Redis命令:

127.0.0.1:6379> incr REDISORDER:NO
(integer) 1
127.0.0.1:6379>

配置文件 
redis:
  increment-key-prefix: REDIS_ORDER:NO
工具类实现
@Component
public class GenerateIDUtils {
    @Component
    public class RedisGenerateIDUtils {
        @Autowired
        private RedisTemplate redisTemplate;
        // 业务前缀
        @Value("${redis.increment-key-prefix}")
        private String BUSINESS_PREFIX = "REDIS_ORDER:NO:";

        /**
         * 获取全局唯一ID
         * @param key 业务标识key
         */
        public String generateId(String key) {
            //年月日
            String yearMoonSun = this.getYearMoonSun();
            // 获取对应业务自增序列
            Long incr = getIncr(key,yearMoonSun);
            // 组装结果
            String resultID = key + yearMoonSun + "-" + incr;
            return resultID;
        }

        /**
         * 组装缓存key
         */
        private String getCacheKey(String key,String yearMoonSun) {
            return BUSINESS_PREFIX + key + ":" + yearMoonSun;
        }

        /**
         * 获取年月日
         */
        private String getYearMoonSun() {
            LocalDate currentDate = LocalDate.now();
            int year = currentDate.getYear();
            int month = currentDate.getMonthValue();
            int day = currentDate.getDayOfMonth();
            return "" + year + month + day;
        }

        /**
         * 获取对应业务自增序列
         */
        private Long getIncr(String key,String yearMoonSun) {
            String cacheKey = getCacheKey(key,yearMoonSun);
            Long increment = 0L;
            // 判断Redis中是否存在这个自增序列,如果不存在添加一个序列并且设置一个过期时间
            if (!redisTemplate.hasKey(cacheKey)) {
                // 这里存在线程安全问题,需要加分布式锁,这里做简单实现
                String lockKey = cacheKey + "_LOCK";
                // 设置分布式锁
                boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 30, TimeUnit.SECONDS);
                if (!lock) {
                    // 如果没有拿到锁进行自旋
                    return getIncr(key,yearMoonSun);
                }
                increment = redisTemplate.opsForValue().increment(cacheKey);
                // 根据实际情况设置当前时间到当天结束时间的插值
                redisTemplate.expire(cacheKey, 24, TimeUnit.HOURS);
                // 释放锁
                redisTemplate.delete(lockKey);
            } else {
                increment = redisTemplate.opsForValue().increment(cacheKey);
            }
            return increment;
        }
    }
}

你可能感兴趣的:(spring,boot,后端)