SpringBoot基于Redis實現生成全局唯一ID的方法
生成全局唯一ID
是一種在分布式系統下用來生成全局唯一id的工具

在項目中生成全局唯一ID有很多好處,其中包括:
- 數據庫主鍵:在數據庫中,唯一ID可以作為主鍵,確保每條記錄的唯一性,便于快速檢索和更新數據。
- 分布式系統:在分布式系統中,生成全局唯一ID可以避免不同節(jié)點生成相同的ID,確保整個系統的數據一致性。
- 日志追蹤:在日志系統中,給每條日志分配唯一ID可以方便進行日志的追蹤和分析。
- 安全性:某些場景下,需要對數據進行加密或者數據權限控制,唯一ID可以作為安全機制的一部分。
- 緩存鍵值:在緩存系統中,使用唯一ID作為鍵值可以避免不同數據之間的沖突。
- 數據分片:在分布式存儲系統中,唯一ID可以作為數據分片的標識,便于數據的存儲和查詢。
總之,生成全局唯一ID有助于提高系統的可用性、數據的完整性和安全性,同時也方便數據的管理和分析。因此,在許多項目中都會需要生成全局唯一ID來滿足系統的需求。
為什么要生成全局唯一id
生成全局唯一ID的主要目的是確保系統中的實體(如對象、記錄、消息等)具有唯一性標識。以下是一些常見的原因:
- 數據唯一性:全局唯一ID可以確保在系統中每個實體都有一個獨一無二的標識符,避免數據沖突和重復。
- 數據庫索引:全局唯一ID通常用作數據庫表的主鍵或索引,以提高數據查詢和檢索的效率。
- 分布式系統:在分布式系統中,各個節(jié)點可能同時生成ID,為了避免ID的沖突,需要使用全局唯一ID算法確保整個系統中的ID唯一性。
- 數據跟蹤與關聯:通過給實體分配唯一ID,可以輕松追蹤和關聯數據,例如日志記錄、事務管理、審計等。
- 安全性和權限控制:全局唯一ID可以用于確保數據的安全性和權限控制,限制對特定實體的訪問和操作。
- 緩存與緩存失效:在緩存系統中,使用全局唯一ID作為緩存鍵,可以確保不同實體之間的鍵不會沖突,并且在緩存失效時能夠正確地重新加載數據。
總結來說,生成全局唯一ID有助于確保數據的唯一性、提高系統的可用性和性能,并支持數據跟蹤、安全性和權限控制等功能。這在許多系統和應用中都是一個重要的需求。
生成全局id的方法

代碼實現
ID生成器的算法如下

我們要先生成時間戳,在生成序列號,然后進行拼接
package com.hmdp.utils;
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 {
/**
* 開始時間戳
*/
private static final long BEGIN_TIMESTAMP = 1640995200L;
/**
* 序列號的位數
*/
private static final int COUNT_BITS = 32;
private StringRedisTemplate stringRedisTemplate;
public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
this.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("icr:" + keyPrefix + ":" + date);
// 3.拼接并返回
return timestamp << COUNT_BITS | count;
}
}
這段代碼的 timestamp << COUNT_BITS | count;是怎么算出序列號的

在這段代碼中,timestamp << COUNT_BITS | count 是通過位運算來生成最終的ID值。
首先,timestamp 是時間戳,代表了從開始時間戳到當前時間的秒數差。COUNT_BITS 是序列號的位數,這里是32位。
位運算符 << 是左移操作符,將 timestamp 的二進制表示向左移動 COUNT_BITS 位,就是將時間戳占據高位。這樣做是為了給序列號騰出足夠的空間。
然后,使用位運算符 | 進行按位或操作,將左移后的時間戳與序列號 count 進行按位或操作,合并它們的二進制表示。
最終得到的結果就是一個64位的ID,其中高位是時間戳部分,低位是序列號部分。
編寫代碼進行測試

package com.hmdp;
import com.hmdp.entity.Shop;
import com.hmdp.service.impl.ShopServiceImpl;
import com.hmdp.utils.CacheClient;
import com.hmdp.utils.RedisIdWorker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;
import static com.hmdp.utils.RedisConstants.SHOP_GEO_KEY;
@SpringBootTest
class HmDianPingApplicationTests {
@Resource
private CacheClient cacheClient;
@Resource
private ShopServiceImpl shopService;
@Resource
private RedisIdWorker redisIdWorker;
private ExecutorService es = Executors.newFixedThreadPool(500);
@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));
}
@Test
void testSaveShop() throws InterruptedException {
Shop shop = shopService.getById(1L);
cacheClient.setWithLogicalExpire(CACHE_SHOP_KEY + 1L, shop, 10L, TimeUnit.SECONDS);
}
}
在技術的道路上,我們不斷探索、不斷前行,不斷面對挑戰(zhàn)、不斷突破自我。科技的發(fā)展改變著世界,而我們作為技術人員,也在這個過程中書寫著自己的篇章。讓我們攜手并進,共同努力,開創(chuàng)美好的未來!愿我們在科技的征途上不斷奮進,創(chuàng)造出更加美好、更加智能的明天!
以上就是SpringBoot基于Redis實現生成全局唯一ID的方法的詳細內容,更多關于SpringBoot Redis全局唯一ID的資料請關注腳本之家其它相關文章!
相關文章
Spring Cloud OAuth2中/oauth/token的返回內容格式
Spring Cloud OAuth2 生成access token的請求/oauth/token的返回內容就需要自定義,本文就詳細介紹一下,感興趣的可以了解一下2021-07-07
在Java的MyBatis框架中建立接口進行CRUD操作的方法
這篇文章主要介紹了在Java的MyBatis框架中建立接口進行CRUD操作的方法,CRUD是指在做計算處理時的增加(Create)、重新取得數據(Retrieve)、更新(Update)和刪除(Delete)幾個單詞的首字母簡寫,需要的朋友可以參考下2016-04-04
SpringBoot中@ConfigurationProperties 配置綁定
本文主要介紹了SpringBoot中@ConfigurationProperties 配置綁定,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
從零開始搭建springboot+springcloud+mybatis本地項目全過程(圖解)
這篇文章主要介紹了從零開始搭建springboot+springcloud+mybatis本地項目全過程(圖解),本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01

