首先我们需要明确的结论:Redis实现ID可以用,这也是很多公司的选择。但是在redis服务器宕机的情况下,他也可能会出现重复生成ID的情况。但是任何方案都不会是十全十美的,这就需要我们做一个取舍,不能因噎废食。
首先我们需要明确一点:redis分布式id实现需要用到命令incr。
1、那么按照我们的思路来说:很简单,对应的key和value, key:业务类型 value:自增加数值。 redis中对应 order=0;
1.1 缺点:订单的value会随着订单的增多一直网上增加,迟早有一天会突破redis数值的极限。(最主要的原因)
1.2 如果某个业务想通过redis统计某一天的订单量,这个其实看不来的。
2、针对方案1的缺点。我们可以改进为针对以每天单位为维度设置为key进行统计,那么可以完美解决上述的方案。那么就诞生了新的的redis分布式id的结构。
key:order:2023:04:06 value: 时间戳( 1位(代表正负数值)+31位 =32位置) + 自增序列(32位)
2.1 针对方案二的实现方案:
@Component
public class RedisUtils {
/**
* 开始时间戳(可以根据自己的需求设定 比如 设定为 2023-04-01 )
*/
private static final long BEGIN_TIMESTAMP = 1640995200L;
/**
* 序列号的位数
*/
private static final int COUNT_BITS = 32;
@Autowired
private StringRedisTemplate stringRedisTemplate;
public long getNextId(String keyPrefix){ // 这个key 可以看做是业务key,不同的业务对应不同的key
// 1. 生成时间戳
long now = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
// 2、减去BEGIN_TIMESTAMP是因为缩小了时间戳的起始值。左移32的时候,起始值变得更小,不容易占满32位置,出现重复
long timeStamp = now - BEGIN_TIMESTAMP;
// 3 获取当前日期,精确到天
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
// 2.2 自增长
// 这里即便没有对应的key,redis也会自动帮我们创建
long count = stringRedisTemplate.opsForValue().increment( keyPrefix + ":" + date);
// 3. 拼接并返回
return timeStamp << COUNT_BITS | count;
}
}
2.2 那么看完代码你能会有以下疑惑
为什么会出现"now - BEGIN_TIMESTAMP",下面会回答你。
/**
*
* 因为:如果now = 1680806345,那么中间32位分布式id就会从1680806345开始,直到占满2^32-1,范围为(1680806345~2^32-1),
* 无形中会浪费1-1680806345。这么多数值。
* 所以:减去BEGIN_TIMESTAMP是因为缩小了时间戳的起始值。不容易占满32位置,范围变为(1680806345~2^32-1)
* 同时随着时间的推移很有可能出现now很大的情况,导致生成重复的分布式ID,从目前来看,占满32位需要69年,那么从1970年算,那么直到2039就会出现重复的ID。
* 2^32-1 / 365 / 24 / 60 /60 大约等于69年
* 下面是举的例子
* 例如: now = 1680806345
* 转换前的数字 二进制
* 1680806345 11001000 01011110 0010001 11001001
* 左移 32的数值
* 7219008282684293120 11001000 01011110 0010001 11001001 00000000 00000000 00000000 00000000
* | count =1
* 7219008282684293000 11001000 01011110 0010001 11001001 00000000 00000000 00000000 00000001
*
*/
2.3 那么方案二有那些优缺点呢
优点:内存数据库,所以生成id快。
缺点:数据库作为一个缓存,很有可能出现宕机的情况,即使redis有持久化机制,但是可能存在丢失1s的情况,那么这种极端场景下可能出现重复生成ID的情况。