SpringBoot基于Redis實(shí)現(xiàn)生成全局唯一ID的方法
生成全局唯一ID
是一種在分布式系統(tǒng)下用來(lái)生成全局唯一id的工具
在項(xiàng)目中生成全局唯一ID有很多好處,其中包括:
- 數(shù)據(jù)庫(kù)主鍵:在數(shù)據(jù)庫(kù)中,唯一ID可以作為主鍵,確保每條記錄的唯一性,便于快速檢索和更新數(shù)據(jù)。
- 分布式系統(tǒng):在分布式系統(tǒng)中,生成全局唯一ID可以避免不同節(jié)點(diǎn)生成相同的ID,確保整個(gè)系統(tǒng)的數(shù)據(jù)一致性。
- 日志追蹤:在日志系統(tǒng)中,給每條日志分配唯一ID可以方便進(jìn)行日志的追蹤和分析。
- 安全性:某些場(chǎng)景下,需要對(duì)數(shù)據(jù)進(jìn)行加密或者數(shù)據(jù)權(quán)限控制,唯一ID可以作為安全機(jī)制的一部分。
- 緩存鍵值:在緩存系統(tǒng)中,使用唯一ID作為鍵值可以避免不同數(shù)據(jù)之間的沖突。
- 數(shù)據(jù)分片:在分布式存儲(chǔ)系統(tǒng)中,唯一ID可以作為數(shù)據(jù)分片的標(biāo)識(shí),便于數(shù)據(jù)的存儲(chǔ)和查詢(xún)。
總之,生成全局唯一ID有助于提高系統(tǒng)的可用性、數(shù)據(jù)的完整性和安全性,同時(shí)也方便數(shù)據(jù)的管理和分析。因此,在許多項(xiàng)目中都會(huì)需要生成全局唯一ID來(lái)滿(mǎn)足系統(tǒng)的需求。
為什么要生成全局唯一id
生成全局唯一ID的主要目的是確保系統(tǒng)中的實(shí)體(如對(duì)象、記錄、消息等)具有唯一性標(biāo)識(shí)。以下是一些常見(jiàn)的原因:
- 數(shù)據(jù)唯一性:全局唯一ID可以確保在系統(tǒng)中每個(gè)實(shí)體都有一個(gè)獨(dú)一無(wú)二的標(biāo)識(shí)符,避免數(shù)據(jù)沖突和重復(fù)。
- 數(shù)據(jù)庫(kù)索引:全局唯一ID通常用作數(shù)據(jù)庫(kù)表的主鍵或索引,以提高數(shù)據(jù)查詢(xún)和檢索的效率。
- 分布式系統(tǒng):在分布式系統(tǒng)中,各個(gè)節(jié)點(diǎn)可能同時(shí)生成ID,為了避免ID的沖突,需要使用全局唯一ID算法確保整個(gè)系統(tǒng)中的ID唯一性。
- 數(shù)據(jù)跟蹤與關(guān)聯(lián):通過(guò)給實(shí)體分配唯一ID,可以輕松追蹤和關(guān)聯(lián)數(shù)據(jù),例如日志記錄、事務(wù)管理、審計(jì)等。
- 安全性和權(quán)限控制:全局唯一ID可以用于確保數(shù)據(jù)的安全性和權(quán)限控制,限制對(duì)特定實(shí)體的訪(fǎng)問(wèn)和操作。
- 緩存與緩存失效:在緩存系統(tǒng)中,使用全局唯一ID作為緩存鍵,可以確保不同實(shí)體之間的鍵不會(huì)沖突,并且在緩存失效時(shí)能夠正確地重新加載數(shù)據(jù)。
總結(jié)來(lái)說(shuō),生成全局唯一ID有助于確保數(shù)據(jù)的唯一性、提高系統(tǒng)的可用性和性能,并支持?jǐn)?shù)據(jù)跟蹤、安全性和權(quán)限控制等功能。這在許多系統(tǒng)和應(yīng)用中都是一個(gè)重要的需求。
生成全局id的方法
代碼實(shí)現(xiàn)
ID生成器的算法如下
我們要先生成時(shí)間戳,在生成序列號(hào),然后進(jìn)行拼接
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 { /** * 開(kāi)始時(shí)間戳 */ private static final long BEGIN_TIMESTAMP = 1640995200L; /** * 序列號(hào)的位數(shù) */ private static final int COUNT_BITS = 32; private StringRedisTemplate stringRedisTemplate; public RedisIdWorker(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate = stringRedisTemplate; } public long nextId(String keyPrefix) { // 1.生成時(shí)間戳 LocalDateTime now = LocalDateTime.now(); long nowSecond = now.toEpochSecond(ZoneOffset.UTC); long timestamp = nowSecond - BEGIN_TIMESTAMP; // 2.生成序列號(hào) // 2.1.獲取當(dāng)前日期,精確到天 String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd")); // 2.2.自增長(zhǎng) long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date); // 3.拼接并返回 return timestamp << COUNT_BITS | count; } }
這段代碼的 timestamp << COUNT_BITS | count;是怎么算出序列號(hào)的
在這段代碼中,timestamp << COUNT_BITS | count 是通過(guò)位運(yùn)算來(lái)生成最終的ID值。
首先,timestamp 是時(shí)間戳,代表了從開(kāi)始時(shí)間戳到當(dāng)前時(shí)間的秒數(shù)差。COUNT_BITS 是序列號(hào)的位數(shù),這里是32位。
位運(yùn)算符 << 是左移操作符,將 timestamp 的二進(jìn)制表示向左移動(dòng) COUNT_BITS 位,就是將時(shí)間戳占據(jù)高位。這樣做是為了給序列號(hào)騰出足夠的空間
。
然后,使用位運(yùn)算符 | 進(jìn)行按位或操作,將左移后的時(shí)間戳與序列號(hào) count 進(jìn)行按位或操作,合并它們的二進(jìn)制表示。
最終得到的結(jié)果就是一個(gè)64位的ID,其中高位是時(shí)間戳部分,低位是序列號(hào)部分。
編寫(xiě)代碼進(jìn)行測(cè)試
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(); //等待上面的結(jié)束 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); } }
在技術(shù)的道路上,我們不斷探索、不斷前行,不斷面對(duì)挑戰(zhàn)、不斷突破自我??萍嫉陌l(fā)展改變著世界,而我們作為技術(shù)人員,也在這個(gè)過(guò)程中書(shū)寫(xiě)著自己的篇章。讓我們攜手并進(jìn),共同努力,開(kāi)創(chuàng)美好的未來(lái)!愿我們?cè)诳萍嫉恼魍旧喜粩鄪^進(jìn),創(chuàng)造出更加美好、更加智能的明天!
以上就是SpringBoot基于Redis實(shí)現(xiàn)生成全局唯一ID的方法的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Redis全局唯一ID的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Cloud OAuth2中/oauth/token的返回內(nèi)容格式
Spring Cloud OAuth2 生成access token的請(qǐng)求/oauth/token的返回內(nèi)容就需要自定義,本文就詳細(xì)介紹一下,感興趣的可以了解一下2021-07-07在Java的MyBatis框架中建立接口進(jìn)行CRUD操作的方法
這篇文章主要介紹了在Java的MyBatis框架中建立接口進(jìn)行CRUD操作的方法,CRUD是指在做計(jì)算處理時(shí)的增加(Create)、重新取得數(shù)據(jù)(Retrieve)、更新(Update)和刪除(Delete)幾個(gè)單詞的首字母簡(jiǎn)寫(xiě),需要的朋友可以參考下2016-04-04SpringBoot中@ConfigurationProperties 配置綁定
本文主要介紹了SpringBoot中@ConfigurationProperties 配置綁定,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11SpringBoot3實(shí)現(xiàn)上傳圖片并返回路徑讓前端顯示圖片
這篇文章主要介紹了SpringBoot3實(shí)現(xiàn)上傳圖片并返回路徑讓前端顯示圖片,文中通過(guò)圖文和代碼講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-12-12詳解Java 中的嵌套類(lèi)與內(nèi)部類(lèi)
這篇文章主要介紹了詳解Java 中的嵌套類(lèi)與內(nèi)部類(lèi)的相關(guān)資料,希望通過(guò)本文大家能掌握J(rèn)ava 嵌套類(lèi)與內(nèi)部類(lèi)的使用方法,需要的朋友可以參考下2017-09-09springboot 獲取訪(fǎng)問(wèn)接口的請(qǐng)求的IP地址的實(shí)現(xiàn)
本文主要介紹了springboot獲取訪(fǎng)問(wèn)接口的請(qǐng)求的IP地址的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07從零開(kāi)始搭建springboot+springcloud+mybatis本地項(xiàng)目全過(guò)程(圖解)
這篇文章主要介紹了從零開(kāi)始搭建springboot+springcloud+mybatis本地項(xiàng)目全過(guò)程(圖解),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01