Redis實現(xiàn)全局唯一Id的使用示例
一、全局唯一Id簡介
系統(tǒng)當(dāng)中有些場景如果使用數(shù)據(jù)庫自增ID就存在一些問題:
- id的規(guī)律性太明顯
- 受單表數(shù)據(jù)量的限制
場景分析:如果我們的id具有太明顯的規(guī)則,用戶或者說商業(yè)對手很容易猜測出來我們的一些敏感信息,比如商城在一天時間內(nèi),賣出了多少單,這明顯不合適。
場景分析二:隨著我們商城規(guī)模越來越大,mysql的單表的容量不宜超過500W,數(shù)據(jù)量過大之后,我們要進(jìn)行拆庫拆表,但拆分表了之后,他們從邏輯上講他們是同一張表,所以他們的id是不能一樣的, 于是乎我們需要保證id的唯一性。
全局唯一ID生成策略:
- UUID
- Redis自增
- snowflake算法
- 數(shù)據(jù)庫自增
Redis自增ID策略:
- 每天一個key,方便統(tǒng)計訂單量
- ID構(gòu)造是 時間戳 + 計數(shù)器
全局ID生成器,是一種在分布式系統(tǒng)下用來生成全局唯一ID的工具,一般要滿足下列特性:

為了增加ID的安全性,我們可以不直接使用Redis自增的數(shù)值,而是拼接一些其它信息:

D的組成部分:符號位:1bit,永遠(yuǎn)為0
時間戳:31bit,以秒為單位,可以使用69年
序列號:32bit,秒內(nèi)的計數(shù)器,支持每秒產(chǎn)生2^32個不同ID
二、Redis實現(xiàn)全局唯一Id實踐
2.1添加RedisIdWorker配置類
package com.example.idgenerate.config;
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 RedisIdWorker {
/**
* 開始時間戳,此處的時間戳為預(yù)生成
*/
private static final long BEGIN_TIMESTAMP = 1640995200L;
/**
* 序列號的位數(shù)
*/
private static final int COUNT_BITS = 32;
private StringRedisTemplate stringRedisTemplate;
public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 描述信息: 生成唯一id
*
* @date 2023/05/21
* @param keyPrefix 前綴,用于區(qū)分不同的業(yè)務(wù)
* @return long id
**/
public long nextId(String keyPrefix) {
// 1.生成時間戳
LocalDateTime now = LocalDateTime.now();
long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
long timestamp = nowSecond - BEGIN_TIMESTAMP;
// 2.生成序列號
// 2.1.獲取當(dāng)前日期,精確到天
String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
// 2.2.自增長 key格式:icr:order:2023:05:21
long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
// 3.拼接并返回,目的是讓時間戳存放在32上,序列號在低32位
return timestamp << COUNT_BITS | count;
}
}

2.2測試類
@Autowired
private RedisIdWorker redisIdWorker;
/**
* 描述信息:將日期時間轉(zhuǎn)成秒級數(shù)字
*
* @date 2023/05/21
* @return void
**/
@Test
void contextLoads() {
LocalDateTime time = LocalDateTime.of(2023, 5, 20, 0, 0,0);
//將時間轉(zhuǎn)成秒數(shù)
long second = time.toEpochSecond(ZoneOffset.UTC);
System.out.println("second = " + second);
}
//實際項目中應(yīng)使用自定義的線程池
private ExecutorService es = Executors.newFixedThreadPool(500);
/**
* 描述信息: 測試redis生成3w條id所需要的時間
*
* @date 2023/05/21
* @return void
**/
@Test
void testIdWorker() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(300);
Runnable task = () -> {
for (int i = 0; i < 100; i++) {
long id = redisIdWorker.nextId("order");
System.out.println("id = " + id);
}
latch.countDown();
};
long begin = System.currentTimeMillis();
for (int i = 0; i < 300; i++) {
es.submit(task);
}
latch.await();
long end = System.currentTimeMillis();
System.out.println("time = " + (end - begin));
}

知識小貼士:關(guān)于countdownlatch
countdownlatch名為信號槍:主要的作用是同步協(xié)調(diào)在多線程的等待于喚醒問題
我們?nèi)绻麤]有CountDownLatch ,那么由于程序是異步的,當(dāng)異步程序沒有執(zhí)行完時,主線程就已經(jīng)執(zhí)行完了,然后我們期望的是分線程全部走完之后,主線程再走,所以我們此時需要使用到CountDownLatch
CountDownLatch 中有兩個最重要的方法
1、countDown
2、await
await 方法是阻塞方法,我們擔(dān)心分線程沒有執(zhí)行完時,main線程就先執(zhí)行,所以使用await可以讓main線程阻塞,那么什么時候main線程不再阻塞呢?當(dāng)CountDownLatch 內(nèi)部維護(hù)的 變量變?yōu)?時,就不再阻塞,直接放行,那么什么時候CountDownLatch 維護(hù)的變量變?yōu)? 呢,我們只需要調(diào)用一次countDown ,內(nèi)部變量就減少1,我們讓分線程和變量綁定, 執(zhí)行完一個分線程就減少一個變量,當(dāng)分線程全部走完,CountDownLatch 維護(hù)的變量就是0,此時await就不再阻塞,統(tǒng)計出來的時間也就是所有分線程執(zhí)行完后的時間。
到此這篇關(guān)于Redis實現(xiàn)全局唯一Id的使用示例的文章就介紹到這了,更多相關(guān)Redis 全局唯一Id內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis+MQ高并發(fā)秒殺的技術(shù)方案與實現(xiàn)
本文提出了一種基于Redis與消息隊列的高并發(fā)秒殺系統(tǒng)解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2026-03-03
Spring?Boot?整合Redis?實現(xiàn)優(yōu)惠卷秒殺?一人一單功能
這篇文章主要介紹了Spring?Boot?整合Redis?實現(xiàn)優(yōu)惠卷秒殺?一人一單,在分布式系統(tǒng)下,高并發(fā)的場景下,會出現(xiàn)此類庫存超賣問題,本篇文章介紹了采用樂觀鎖來解決,需要的朋友可以參考下2022-09-09
Redis實戰(zhàn)之Redis實現(xiàn)異步秒殺優(yōu)化詳解
這篇文章主要給大家介紹了Redis實戰(zhàn)之Redis實現(xiàn)異步秒殺優(yōu)化方法,文章通過圖片和代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,感興趣的同學(xué)可以自己動手試一下2023-09-09
多維度深入分析Redis的5種基本數(shù)據(jù)結(jié)構(gòu)
此篇文章主要對Redis的5種基本數(shù)據(jù)類型,即字符串(String)、列表(List)、散列(Hash)、集合(Set)、有序集合(Sorted?Set),從使用場景和底層結(jié)構(gòu)出發(fā),進(jìn)行多維度深入分析。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-11-11

