Redis实现全局唯一Id

一、全局唯一ID

每个店铺都可以发布优惠券:

当用户抢购时,就会生成订单并保存到tb_voucher_order这张表中,而订单表如果使用数据库自增ID就存在一些问题:

  • id的规律性太明显

  • 受单表数据量的限制

场景分析:如果我们的id具有太明显的规则,用户或者说商业对手很容易猜测出来我们的一些敏感信息,比如商城在一天时间内,卖出了多少单,这明显不合适。

场景分析二:随着我们商城规模越来越大,mysql的单表的容量不宜超过500W,数据量过大之后,我们要进行拆库拆表,但拆分表了之后,他们从逻辑上讲他们是同一张表,所以他们的id是不能一样的, 于是乎我们需要保证id的唯一性。

全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足下列特性:

为了增加ID的安全性,我们可以不直接使用Redis自增的数值,而是拼接一些其它信息:

成部分:符号位:1bit,永远为0

时间戳:31bit,以秒为单位,可以使用69年

序列号:32bit,秒内的计数器,支持每秒产生2^32个不同ID

二、Redis实现全局唯一Id

复制代码
package com.dfrz.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

@Component
public class MyRedisIncreaseIdUtil {
    /**
     * 开始时间戳,2022年1月1日0时0分0秒的时间的时间戳
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L;
    /**
     * 序列号的位数
     */
    private static final int COUNT_BITS = 32;

    private static final String INCREASE_ID_PRE = "increase:id";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    public long nextId(String keyPrefix) {
        // 1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond - BEGIN_TIMESTAMP;

        // 2.生成序列号
        // 2.1.获取当前日期,精确到天
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        // 2.2.自增长
        long count = stringRedisTemplate.opsForValue().increment(INCREASE_ID_PRE + keyPrefix + ":" + date);

        // 3.拼接并返回
        return timestamp << COUNT_BITS | count;
    }

   /* public static void main(String[] args) {
        //使用LocalDateTime.of()方法创建了一个LocalDateTime对象,代表2022年1月1日0时0分0秒的时间
        LocalDateTime time = LocalDateTime.of(2022, 1, 1, 0, 0, 0);
        //使用toEpochSecond()方法将这个时间转换为从1970年1月1日0时0分0秒(UTC时间)开始的秒数
        long second = time.toEpochSecond(ZoneOffset.UTC);
        System.out.println(second);
    }*/

}

测试类:

三、详细介绍

这段代码定义了一个名为MyRedisIncreaseIdUtil的类,用于生成基于时间和Redis自增操作的唯一ID。

具体来说,该类做了以下几件事情:

  1. 定义常量

    • BEGIN_TIMESTAMP:这是开始时间戳,代表2022年1月1日0时0分0秒的时间的时间戳。
    • COUNT_BITS:这是序列号的位数,这里设置为32位。
    • INCREASE_ID_PRE:这是一个字符串常量,用于在Redis中作为键的前缀。
  2. 注入依赖

    • 使用@Autowired注解将StringRedisTemplate对象注入到stringRedisTemplate成员变量中,这是Spring框架的自动装配特性。
  3. 实现生成ID的方法

    • nextId(String keyPrefix):这是一个公共方法,接受一个字符串参数keyPrefix,并返回一个长整型ID。
      • 首先,使用LocalDateTime.now()获取当前时间,然后通过toEpochSecond(ZoneOffset.UTC)方法将其转换为Unix时间戳(从1970年1月1日0时0分0秒开始的秒数)。
      • 接着,使用now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"))获取当前日期,精确到天,并格式化为"yyyy:MM:dd"的字符串格式。
      • 然后,使用stringRedisTemplate.opsForValue().increment()对Redis中的键进行原子自增操作。这里使用了格式化的日期作为Redis键的一部分,以确保每个日期只有一个序列号。
      • 最后,通过位运算(左移操作符<<)将时间戳转换为二进制表示中的高位,序列号转换为低位,然后使用位或操作符|将它们拼接在一起,形成一个唯一的ID。

这个类的主要目的是生成基于当前时间和Redis自增操作的唯一ID,通过时间戳保证全局唯一性,通过Redis自增操作保证同一日期内的唯一性。

相关推荐
一叶飘零_sweeeet3 小时前
从手写 Redis 分布式锁到精通 Redisson:分布式系统的并发控制终极指南
redis·分布式·redisson
睡觉的时候不会困3 小时前
Redis 主从复制详解:原理、配置与主从切换实战
数据库·redis·bootstrap
程序员的世界你不懂5 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
自学也学好编程5 小时前
【数据库】Redis详解:内存数据库与缓存之王
数据库·redis
JAVA不会写6 小时前
在Mybatis plus中如何使用自定义Sql
数据库·sql
IT 小阿姨(数据库)6 小时前
PgSQL监控死元组和自动清理状态的SQL语句执行报错ERROR: division by zero原因分析和解决方法
linux·运维·数据库·sql·postgresql·centos
ChinaRainbowSea6 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
小马学嵌入式~7 小时前
嵌入式 SQLite 数据库开发笔记
linux·c语言·数据库·笔记·sql·学习·sqlite
Java小白程序员8 小时前
MyBatis基础到高级实践:全方位指南(中)
数据库·mybatis
Monly218 小时前
人大金仓:merge sql error, dbType null, druid-1.2.20
数据库·sql