Redis Incr 命令会将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
这里以jedis为例提供两种自增ID的生成方式
第一种方式直接通过Incr命令获取自增ID
JedisUtils.incr("inc-test1")
第二张方式获取带有时间戳的唯一编码,时间细度为分钟
/**
* 基于Redis生成 时间+递增ID 的唯一编码
* @param key
* @return
*/
public static String getRedisTimeSeq(String key) {
String time = DateUtils.parseDateToStr("yyyyMMddHHmm",new Date());
StringBuilder sBuilder = new StringBuilder(time);
sBuilder.append(JedisUtils.incr(key+":"+time,120));//保证一分钟内KEY有效
return sBuilder.toString();
}
二、测试
下面我们从冲突率与时间性能上,对以上几种唯一ID的生成方式进行一个简单的测试,同时基于并发安全的考虑,测试分为单线程与多线程两种
// ---------------测试---------------
public static void main(String[] args) throws InterruptedException {
int length = 10000000;
SnowFlake snowFlake = new SnowFlake(1, 1);
final CountDownLatch countDownLatch = new CountDownLatch(10);
Map
long begin = System.currentTimeMillis();
for(int i=0;i<10;i++) {
Thread thread = new Thread(new Runnable() {
public void run() {
for (int i = 0; i != 1000000; ++i) {
String str = String.valueOf(snowFlake.nextId());
// String str = StringUtils.getUUIDHashCode(); //根据UUID产生命令唯一标识 长度 32 字母数字组合
//
// String str = StringUtils.getOrderSeq();//根据UUID取hash值+随机数,产生命令唯一标识 长度 16位纯数字
//
// String str =StringUtils.getRandomHexString(); //长度16的16进制字符串
map.put(str, str);
}
countDownLatch.countDown();
}
});
thread.start();
}
countDownLatch.await();
System.out.println("冲突数为: " + (length - map.size()));
System.out.println("sync time: " + (System.currentTimeMillis() - begin));
Map
begin = System.currentTimeMillis();
for (int i = 0; i != length; ++i) {
String str = String.valueOf(snowFlake.nextId());
// String str = StringUtils.getUUIDHashCode();//根据UUID产生命令唯一标识
// String str = StringUtils.getOrderSeq();//根据UUID取hash值+随机数,产生命令唯一标识
// String str =StringUtils.getRandomHexString();
map1.put(str, str);
}
System.out.println("冲突数为: " + (length - map1.size()));
System.out.println("sync time: " + (System.currentTimeMillis() - begin));
}
测试结果如下:
生成方式
生成总数
并发
冲突数
耗时
UUID产生命令唯一标识
1000W
单线程
0
26166ms
UUID产生命令唯一标识
1000W
多线程
0
27915ms
根据UUID取hash值+随机数,产生命令唯一标识
1000W
单线程
0
25405ms
根据UUID取hash值+随机数,产生命令唯一标识
1000W
多线程
0
25023ms
十六位随机的十六进制字符串
1000W
单线程
0
25723ms
十六位随机的十六进制字符串
1000W
多线程
0
28094ms
雪花算法
1000W
单线程
0
10100ms
雪花算法
1000W
多线程
0
11713ms
针对 Redis Incr 命令进行了本地和局域网两种测试, 由于千万级数据耗时太长,数据量改为了百万级,结果如下:
生成方式
网络环境
生成总数
并发
冲突数
耗时
Redis Incr命令获取自增ID
本地
100W
单线程
0
72445ms
Redis Incr命令获取自增ID
本地
100W
多线程
0
47879ms
Redis Incr命令获取自增ID
局域网
100W
单线程
0
71447ms
Redis Incr命令获取自增ID
局域网
100W
多线程
0
45888ms
Redis Incr命令生成 时间+递增ID 的唯一编码
局域网
100W
单线程
0
236795ms
Redis Incr命令生成 时间+递增ID 的唯一编码
局域网
100W
多线程
0
39281ms
可以看到Redis相比前面一些轻量级的ID生成方式,生成效率上有明显差距,但在分布式环境下,且业务场景对全局唯一ID的生成样式有要求, redis做为统一的ID生成器还是很有必要的。
由于测试受机器配置、网络带宽等条件影响,以上得出的结果只是一个简单的测试结果,证明这几种唯一ID生成方式具备一定的可用性,大家如果有兴趣可以进行更深入的测试与优化;
三、总结
其实在日常开发中唯一ID的的生成方式与整体服务的架构与复杂度是密切相关的,本文从并发安全、冲突率、性能等多个方面列举了几种唯一ID的生成方式,相对比较简单实用,但在更复杂的架构与业务场景下,对唯一ID生成的方式的考量就需要更加全面,如并发量、持久化、获取方式等,都需要具体问题具体分析,这里不再深入探讨,希望本文对大家能有所帮助,其中如有不足与不正确的地方还望指出与海涵。
关注微信公众号,查看更多技术文章。