Redis学习(三):全局唯一ID
1. 全局唯一ID的特性
全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足下列特性:
- 唯一性
- 高可用性
- 递增性
- 安全性
- 高性能
2. Redis生成全局唯一ID分析
- 在Redis中,可以使用
incr
命令将某个键的值加1,这个操作是原子性的,能够有效解决并发问题。 - 然而,直接将每次
incr
后的值作为ID并不安全,因为这类似于数据库的自增ID,可能存在安全隐患。 - 为了提高安全性,我们可以将自增的值作为序列号,并在前面加上时间戳,形成一个唯一的ID。
- 通常,我们使用
long
类型来存储ID,这样ID就是一个64位的二进制数。 - 由于ID中包含了时间戳,因此序列号占用的位数是有限的。随着时间的推移,序列号会达到最大值。为了避免这种情况,我们需要周期性地更换Redis中的键。
- 一种常见的做法是,通过将日期拼接到键名中来实现周期性更换。例如,可以将日期作为键的一部分,这样每过一段时间就会自动更换键。
- 在实际开发中,一个ID通常由3部分组成:1位符号位、31位时间戳和32位序列号。
- 为了实现周期性更换,可以使用类似
incr:id:20241212
这样的键名,其中20241212
表示日期,这样每一天都会生成一个新的键,从而避免序列号溢出。
3. Redis生成全局唯一ID实现
@Component
public class RedisIdWorker {
// 开始时间戳
private static final long BEGIN_TIMESTAMP = 1684224000L;
// 序列号的位数
private static final int COUNT_BITS = 32;
private final StringRedisTemplate stringRedisTemplate;
public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
public long nextId(String keyPrefix) {
// 1. 生成时间戳
LocalDateTime now = LocalDateTime.now();
long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
long timestamp = nowSecond - BEGIN_TIMESTAMP;
System.out.println("timestamp:" + timestamp);
// 2. 生成序列号
// 2.1 获取当前日期,精确到天
String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
// 2.2 自增长
Long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
System.out.println("count:" + count);
// 3. 拼接返回id
return timestamp << COUNT_BITS | count;
}
}
测试用例:
@SpringBootTest
public class RedisIdWorkerTest {
@Autowired
private RedisIdWorker redisIdWorker;
private final ExecutorService es = Executors.newFixedThreadPool(500);
@Test
public void nextIdTest() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(300);
Runnable task = () -> {
for (int i = 0; i < 100; i++) {
long id = redisIdWorker.nextId("order");
System.out.println("id = " + id);
}
latch.countDown();
};
long begin = System.currentTimeMillis();
for (int i = 0; i < 300; i++) {
es.submit(task);
}
latch.await();
long end = System.currentTimeMillis();
System.out.println("time = " + (end - begin));
}
}
本文是原创文章,转载请注明来自 Lazyking.site
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果
Steam卡片