3

简单实用!利用Redis轻松实现高并发全局ID生成器

 1 year ago
source link: https://www.51cto.com/article/743438.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
06569db86ed9c0e8e4e658959c761dd0fdec38.png

Redis作为高性能的KV数据库,并且操作还是原子性的,所以用来做支持高并发的发号器十分合适。

本文给大家介绍3种常见的全局ID生成方式。

1、全局递增ID

目标:一直递增的全局ID。

/**
 * 一直递增的全局id
 *
 * @param redisTemplate redis客户端对象
 * @param busId         业务id,可以按需配置
 * @param step          步长,即每次递增的间隔
 */
public static String getNo(RedisTemplate<String, Object> redisTemplate, String busId, int step) {
  //保存redis中的key,注意不要重复
  String redisKey = "uniqueNo_";
  //利用increment即redis原生incrBy命令的原子性特性生成递增的序列号
  Long increment = redisTemplate.opsForValue().increment(redisKey, step);
  if (increment == null) {
    throw new RuntimeException("redis命令执行失败");
  }
  //业务id+递增id,如果需要纯数字,去掉业务id即可
  return busId + increment;
}

2、以天为分割的全局ID

目标:生成格式为 yyyyMMdd + 递增序列号的全局ID。

/**
 * 以天为间隔的递增序列号
 * @param redisTemplate redis客户端对象
 * @param busId 业务id,可以按需配置
 * @param step 步长,即每次递增的间隔
*/
public static String getNo(RedisTemplate<String, Object> redisTemplate, String busId, int step) {
  //当天日期,比如20221226
  String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
  //保存redis中的key,注意不要重复
  String redisKey = "uniqueNo_" + date;
  //利用increment即redis原生incrBy命令的原子性特性生成递增的序列号
  Long increment = redisTemplate.opsForValue().increment(redisKey, step);
  if (increment == null) {
    throw new RuntimeException("redis命令执行失败");
  }
  if (step == increment.intValue()) {
    //首次执行时,给redisKey设置ttl,第二天这个key就可以被redis自动删除
    redisTemplate.expire(redisKey, 25, TimeUnit.HOURS);
  }
  //组合  20221226 + 业务id + 0001(可以根据需要自由调整序列号的长度)
  return date + busId + String.format("%04d", increment);
}

3、批量获取ID

有时我们需要批量的获取递增ID,比如给一批订单号设置ID。

/**
 * 批量获取id
 *
 * @param redisTemplate redis客户端对象
 * @param busId         业务id,可以按需配置
 * @param size          获取的id个数,与步长类似
 */
public static List<String> getNoByGroup(RedisTemplate<String, Object> redisTemplate, String busId, int size) {
  //保存redis中的key,注意不要重复
  String redisKey = "uniqueNo_group";
  //设置步长为size,相当于一次性申请size个id
  Long increment = redisTemplate.opsForValue().increment(redisKey, size);
  if (increment == null) {
    throw new RuntimeException("redis命令执行失败");
  }
  long begin = increment - Long.parseLong(size + "");
  List<String> rs = new ArrayList<>();
  for (long i = begin + 1; i <= increment; i++) {
    rs.add(busId + i);
  }
  return rs;
}

无论我们需要什么格式的ID,其实只要我们把握住其中的核心:incrBy命令,根据其原子性的特性,就可以生成我们需要的全局ID。

但是需要注意的是,虽然incrBy命令是原子性的,但是通过组合键进行组合时,其实是破坏了这种原子性。如果有特殊的ID格式要求,务必要进行充分的测试。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK