每个店铺都可以发布优惠券:
当用户抢购时,就会生成订单并保存到tb_voucher_order这张表中,而订单表如果使用数据库自增ID就存在一些问题:
id的规律性太明显
受单表数据量的限制
场景分析:如果我们的id具有太明显的规则,用户或者说商业对手很容易猜测出来我们的一些敏感信息,比如商城在一天时间内,卖出了多少单,这明显不合适。
场景分析二:随着我们商城规模越来越大,mysql的单表的容量不宜超过500W,数据量过大之后,我们要进行拆库拆表,但拆分表了之后,他们从逻辑上讲他们是同一张表,所以他们的id是不能一样的, 于是乎我们需要保证id的唯一性。
全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足下列特性:
为了增加ID的安全性,我们可以不直接使用Redis自增的数值,而是拼接一些其它信息:
成部分:符号位:1bit,永远为0
时间戳:31bit,以秒为单位,可以使用69年
序列号:32bit,秒内的计数器,支持每秒产生2^32个不同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。
具体来说,该类做了以下几件事情:
定义常量:
BEGIN_TIMESTAMP
:这是开始时间戳,代表2022年1月1日0时0分0秒的时间的时间戳。COUNT_BITS
:这是序列号的位数,这里设置为32位。INCREASE_ID_PRE
:这是一个字符串常量,用于在Redis中作为键的前缀。注入依赖:
@Autowired
注解将StringRedisTemplate
对象注入到stringRedisTemplate
成员变量中,这是Spring框架的自动装配特性。实现生成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自增操作保证同一日期内的唯一性。