Java分布式ID-Redis实现ID

首先我们需要明确的结论: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的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值