本篇分享内容是关于生成分布式 Id 的其中之一方案, 除了 Redis 方案之外还有如: 数据库, 雪花算法, mogodb(object_id 也是数据库) 等方案, 对于 Redis 来说是我们常用并接触比较多的, 因此主要谈谈结合 Redis 生成分布式 id 方案.
分布式 Id 设计流程图
基于 Redis 的 hash 自动 increment 累加生成有序 Id
定期删除无用 hash 列
分布式 Id 设计流程图 (有点粗略)
基于 Redis 的 hash 自动 increment 累加生成有序 Id
使用 Redis 方案生成 id, 其中之一的方式主要使用 increment(递增), 不管是 string,hash 等都具有该方法, 为了更方便管理我们 id 生成 key 这里建议使用 hash 的列的方式, 以下内容都基于 springboot 分享;
当然, 第一步我们需要创建一个 hash 和 hkey 才行, 至于在业务第一次被访问来创建这个 hash 还是通过服务自动创建这个看业务和流量, 这里的 hkey 是有一定规则的 (当然不用局限性), 这里我按照日期格式来做 key, 可以有如下代码:
- /**
- * 生成每天的初始 Id
- * @param hashName
- * @return
- */
- public String initPrimaryId(String hashName) {
- Assert.hasLength(hashName, "hashName 不能为空");
- String hashCol = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
- // 自定义编号规则
- String hashColVal = hashCol + "00001";
- redisTemplate.opsForHash().putIfAbsent(hashName, hashCol, hashColVal);
- return hashCol;
- }
上面很容易理解, hash 中 key 是有每天日期格式组成, 意思每天都需要生成一个新的日期 key, 通过 putIfAbsent 达到不重复添加的原则, 至于 hval 可以根据自定义编号规则来生成一串数字字符 (注: 一定要数字); 有了上面的基础, 我们仅仅需要 increment 来累加, Redis 即帮我们完整 hval+1 的操作, 当然可以自定义累加数, 如下代码:
- /**
- * 获取分布式 Id
- *
- * @param hashName
- * @return
- */
- public long getPrimaryId(String hashName) {
- try {
- String hashCol = initPrimaryId(hashName);
- return redisTemplate.opsForHash().increment(hashName, hashCol, 1);
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- return 0;
- }
定期删除无用 hash 列
就上面我们通过 hash 来设置每天 id 只增初始值, hash 的 hkey 布局用自动过期功能, 因此我们需要代码中维护一套清除来 hkey 的机制, 既然 id 是根据日期生成, 我们可以就用往前推 n 天的方式达到清除老 hkey 目的:
- /**
- * 删除多少天之前的 cols
- * @param hashName
- * @param lessDay
- * @return
- */
- public Long removePrimaryByLessDay(String hashName, int lessDay) {
- try {
- // 当前日期
- String hashCol = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
- long idl = Long.valueOf(hashCol) - lessDay;
- String[] removeCols = redisTemplate.opsForHash().entries(hashName).keySet().stream().
- map(key -> key.toString()).
- filter(key -> idl> Long.valueOf(key)). // 从 + 1 开始, 避免删除当天数据
- toArray(String[]::new);
- if (ArrayUtils.isNotEmpty(removeCols)) {
- return redisTemplate.opsForHash().delete(hashName, removeCols);
- }
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- return 0L;
- }
按照日期来生成分布式 id, 达到 id 不重复的目的, 这也就是分布式 id(不重复), 看起来简单其实如果在高流量冲击下, 需要考虑的东西要很多, 比如: 什么时候生成初始 Id, 在多个服务器保证服务器时间尽可能一样情况下, 该保留多少日期 hkey 等;
就上面代码对初始 Id 就做的不是很好, 在业务获取 Id 时候, 会去检测并创建 id, 这样与 Redis 交互就多了一次, 通常可以用服务来一次性生成当前日期往后推 n 天的 hkey, 这样就避免了在业务获取 id 时候, 还要去 putIfAbsent 一次验证, 减少了请求次数. 实在不行可以使用 lua 脚本放在一次请求去做 put 和 increment, 你可能会用到:
- RedisScript script = new DefaultRedisScript("");
- redisTemplate.execute(script, Arrays.asList(""));
来源: https://www.cnblogs.com/wangrudong003/p/11502305.html