订单号
ID的组成部分: 符号位、时间戳、序列号
符号位: 1bit,永远为0
时间戳: 31bit,以秒为单位,可以使用69年
序列号: 32bit,秒内的计数器,支持每秒产生2^32个不同ID
private static final long BEGIN_TIMESTAMP = 1704067200L; // 初始时间因为是固定的,所以放在常量
private static final int COUNT_BITS = 32; // 序列号的位数
public static void main(String[] args) {
// 初始时间
LocalDateTime begin = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
long beginSecond = begin.toEpochSecond(ZoneOffset.UTC);
System.out.println("beginSecond = " + beginSecond);
LocalDateTime now = LocalDateTime.now();
long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
System.out.println("nowSecond = " + nowSecond);
long timestamp = nowSecond - beginSecond;
System.out.println("nowSecond - beginSecond = " + timestamp);
// 获取当前日期 精确到天
String yyyyMMdd = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
// INCR key 因为key icr:order:年:月:日 自增每天都会重置不用担心会炸掉
long increment = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + yyyyMMdd);
long l = timestamp << COUNT_BITS | increment;
/** 时间戳向左移32,右边拿来放序列号
* ex: 100 << 5 | 2 = 100 * 2^5 + 2 = 3202
* 高位:存储重要信息(如时间戳),通常占据更左侧的位置
* 低位:存储次要信息(如递增计数器),通常占据更右侧的位置
*/
l = timestamp << COUNT_BITS | 1L;
System.out.println("l = " + l);
// 107577278518525953
// 0001 0111 1110 0011 0000 1111 0111 0000 0000 0000 0000 0000 0000 0000 0001
// 2 的0次方 * 1 = 1
l = timestamp << COUNT_BITS | 2L;
System.out.println("l = " + l);
// 107577278518525954
// 0001 0111 1110 0011 0000 1111 0111 0000 0000 0000 0000 0000 0000 0000 0010
// 2 的1次方 * 1 = 2
}