Redis實(shí)現(xiàn)全局唯一Id的使用示例
一、全局唯一Id簡介
系統(tǒng)當(dāng)中有些場景如果使用數(shù)據(jù)庫自增ID就存在一些問題:
- id的規(guī)律性太明顯
- 受單表數(shù)據(jù)量的限制
場景分析:如果我們的id具有太明顯的規(guī)則,用戶或者說商業(yè)對(duì)手很容易猜測出來我們的一些敏感信息,比如商城在一天時(shí)間內(nèi),賣出了多少單,這明顯不合適。
場景分析二:隨著我們商城規(guī)模越來越大,mysql的單表的容量不宜超過500W,數(shù)據(jù)量過大之后,我們要進(jìn)行拆庫拆表,但拆分表了之后,他們從邏輯上講他們是同一張表,所以他們的id是不能一樣的, 于是乎我們需要保證id的唯一性。
全局唯一ID生成策略:
- UUID
- Redis自增
- snowflake算法
- 數(shù)據(jù)庫自增
Redis自增ID策略:
- 每天一個(gè)key,方便統(tǒng)計(jì)訂單量
- ID構(gòu)造是 時(shí)間戳 + 計(jì)數(shù)器
全局ID生成器,是一種在分布式系統(tǒng)下用來生成全局唯一ID的工具,一般要滿足下列特性:
為了增加ID的安全性,我們可以不直接使用Redis自增的數(shù)值,而是拼接一些其它信息:
D的組成部分:符號(hào)位:1bit,永遠(yuǎn)為0
時(shí)間戳:31bit,以秒為單位,可以使用69年
序列號(hào):32bit,秒內(nèi)的計(jì)數(shù)器,支持每秒產(chǎn)生2^32個(gè)不同ID
二、Redis實(shí)現(xiàn)全局唯一Id實(shí)踐
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 { /** * 開始時(shí)間戳,此處的時(shí)間戳為預(yù)生成 */ 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; } /** * 描述信息: 生成唯一id * * @date 2023/05/21 * @param keyPrefix 前綴,用于區(qū)分不同的業(yè)務(wù) * @return long id **/ 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.自增長 key格式:icr:order:2023:05:21 long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date); // 3.拼接并返回,目的是讓時(shí)間戳存放在32上,序列號(hào)在低32位 return timestamp << COUNT_BITS | count; } }
2.2測試類
@Autowired private RedisIdWorker redisIdWorker; /** * 描述信息:將日期時(shí)間轉(zhuǎn)成秒級(jí)數(shù)字 * * @date 2023/05/21 * @return void **/ @Test void contextLoads() { LocalDateTime time = LocalDateTime.of(2023, 5, 20, 0, 0,0); //將時(shí)間轉(zhuǎn)成秒數(shù) long second = time.toEpochSecond(ZoneOffset.UTC); System.out.println("second = " + second); } //實(shí)際項(xiàng)目中應(yīng)使用自定義的線程池 private ExecutorService es = Executors.newFixedThreadPool(500); /** * 描述信息: 測試redis生成3w條id所需要的時(shí)間 * * @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)); }
知識(shí)小貼士:關(guān)于countdownlatch
countdownlatch名為信號(hào)槍
:主要的作用是同步協(xié)調(diào)在多線程的等待于喚醒問題
我們?nèi)绻麤]有CountDownLatch
,那么由于程序是異步的,當(dāng)異步程序沒有執(zhí)行完時(shí),主線程就已經(jīng)執(zhí)行完了,然后我們期望的是分線程全部走完之后,主線程再走,所以我們此時(shí)需要使用到CountDownLatch
CountDownLatch
中有兩個(gè)最重要的方法
1、countDown
2、await
await 方法是阻塞方法,我們擔(dān)心分線程沒有執(zhí)行完時(shí),main線程就先執(zhí)行,所以使用await可以讓main線程阻塞,那么什么時(shí)候main線程不再阻塞呢?當(dāng)CountDownLatch 內(nèi)部維護(hù)的 變量變?yōu)?時(shí),就不再阻塞,直接放行,那么什么時(shí)候CountDownLatch 維護(hù)的變量變?yōu)? 呢,我們只需要調(diào)用一次countDown ,內(nèi)部變量就減少1,我們讓分線程和變量綁定, 執(zhí)行完一個(gè)分線程就減少一個(gè)變量,當(dāng)分線程全部走完,CountDownLatch 維護(hù)的變量就是0,此時(shí)await就不再阻塞,統(tǒng)計(jì)出來的時(shí)間也就是所有分線程執(zhí)行完后的時(shí)間。
到此這篇關(guān)于Redis實(shí)現(xiàn)全局唯一Id的使用示例的文章就介紹到這了,更多相關(guān)Redis 全局唯一Id內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Redis位圖實(shí)現(xiàn)系統(tǒng)用戶登錄統(tǒng)計(jì)
這篇文章主要介紹了基于Redis位圖實(shí)現(xiàn)系統(tǒng)用戶登錄統(tǒng)計(jì),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11Redis高階使用消息隊(duì)列分布式鎖排行榜等(高階用法)
在大多數(shù)傳統(tǒng)的web系統(tǒng)中,使用Redis一般都是作為緩存使用,在大數(shù)據(jù)查詢時(shí)作為緩解性能的一種解決方案,這篇文章主要介紹了Redis高階使用消息隊(duì)列分布式鎖排行榜等,需要的朋友可以參考下2024-03-03關(guān)于redis可視化工具讀取數(shù)據(jù)亂碼問題
大家來聊一聊在日常操作redis時(shí)用的是什么工具,redis提供的一些命令你都了解了嗎,今天通過本文給大家介紹redis可視化工具讀取數(shù)據(jù)亂碼問題,感興趣的朋友跟隨小編一起看看吧2021-07-07Spring?Boot?3.0x的Redis?分布式鎖的概念和原理
Redis?分布式鎖是一種基于?Redis?的分布式鎖解決方案,它的原理是利用?Redis?的原子性操作實(shí)現(xiàn)鎖的獲取和釋放,從而保證共享資源的獨(dú)占性,這篇文章主要介紹了適合?Spring?Boot?3.0x的Redis?分布式鎖,需要的朋友可以參考下2024-08-08利用Redis統(tǒng)計(jì)網(wǎng)站在線活躍用戶的方法
Redis支持對(duì)String類型的value進(jìn)行基于二進(jìn)制位的置位操作。通過將一個(gè)用戶的id對(duì)應(yīng)value上的一位,通過對(duì)活躍用戶對(duì)應(yīng)的位進(jìn)行置位,就能夠用一個(gè)value記錄所有活躍用戶的信息。下面這篇文章主要介紹了利用Redis統(tǒng)計(jì)網(wǎng)站在線活躍用戶的方法,需要的朋友可以參考。2017-01-01詳解Redis中key的命名規(guī)范和值的命名規(guī)范
這篇文章主要介紹了詳解Redis中key的命名規(guī)范和值的命名規(guī)范,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Redis事務(wù)涉及的watch、multi等命令詳解
這篇文章主要介紹了Redis事務(wù)涉及的watch、multi等命令,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2018-10-10