Redis學習打卡-Day3-分布式ID生成策略、分布式鎖

分布式 ID

  • 當單機 MySQL 已經無法支撐系統的數據量時,就需要進行分庫分表(推薦 Sharding-JDBC)。在分庫之后, 數據遍布在不同服務器上的數據庫,數據庫的自增主鍵已經沒辦法滿足生成的主鍵全局唯一。這個時候就需要生成分布式 ID了。

分布式 ID 應滿足的需求

  • 一個最基本的分布式 ID 需要滿足下面這些要求:
    • 全局唯一:ID 的全局唯一性肯定是首先要滿足的!
    • 高性能:分布式 ID 的生成速度要快,對本地資源消耗要小。
    • 高可用:生成分布式 ID 的服務要保證可用性無限接近于 100%。
    • 有序遞增:如果要把 ID 存放在數據庫的話,ID 的有序性可以提升數據庫寫入速度。并且很多時候 ,我們還很有可能會直接通過 ID 來進行排序。
  • 除了這些之外,一個比較好的分布式 ID 還應保證:
    • 安全:ID 中不包含敏感信息。
    • 方便易用:拿來即用,使用方便,快速接入!
    • 有具體的業務含義:生成的 ID 如果能有具體的業務含義,可以讓定位問題以及開發更透明化(通過 ID 就能確定是哪個業務)。
    • 獨立部署:也就是分布式系統單獨有一個發號器服務,專門用來生成分布式 ID。這樣就生成 ID 的服務可以和業務相關的服務解耦。不過,這樣同樣帶來了網絡調用消耗增加的問題。總的來說,如果需要用到分布式 ID 的場景比較多的話,獨立部署的發號器服務還是很有必要的。

分布式 ID 的生成策略

1)UUID
  • UUID 是 Universally Unique Identifier(通用唯一標識符) 的縮寫。UUID 包含 32 個 16 進制數(8-4-4-4-12)。
  • JDK 就提供了現成的生成 UUID 的方法,一行代碼就行了。
    //輸出示例:cb4a9ede-fa5e-4585-b9bb-d60bce986eaa
    UUID.randomUUID()
    
  • 優點:生成速度通常比較快、簡單易用。
  • 缺點:
    • 存儲消耗空間大(32 個字符串,128 位)。
    • 不安全(基于 MAC 地址生成 UUID 的算法會造成 MAC 地址泄露)。
    • 無序(非自增)。
    • 沒有具體業務含義。
    • 需要解決重復 ID 問題(當機器時間不對的情況下,可能導致會產生重復 ID)。
2)Snowflake(雪花算法)
  • Snowflake 是 Twitter 開源的分布式 ID 生成算法。Snowflake 由 64 bit 的二進制數字組成,這 64bit 的二進制被分成了幾部分,每一部分存儲的數據都有特定的含義:
    Snowflake
    • sign(1bit):符號位(標識正負),始終為 0,代表生成的 ID 為正數。
    • timestamp (41 bits):一共 41 位,用來表示時間戳,單位是毫秒,可以支撐 2 41 毫秒(約 69 年)。
    • datacenter id + worker id (10 bits):一般來說,前 5 位表示機房 ID,后 5 位表示機器 ID(實際項目中可以根據實際情況調整),這樣就可以區分不同集群/機房的節點。
    • sequence (12 bits):一共 12 位,用來表示序列號。 序列號為自增值,代表單臺機器每毫秒能夠產生的最大 ID 數(212 = 4096),也就是說單臺機器每毫秒最多可以生成 4096 個 唯一 ID。
  • 優點:生成速度比較快、生成的 ID 有序遞增、比較靈活(可以對 Snowflake 算法進行簡單的改造比如加入業務 ID)。
  • 缺點:
    • 需要解決重復 ID 問題(ID 生成依賴時間,在獲取時間的時候,可能會出現時間回撥的問題,也就是服務器上的時間突然倒退到之前的時間,進而導致會產生重復 ID)。
    • 依賴機器 ID 對分布式環境不友好(當需要自動啟停或增減機器時,固定的機器 ID 可能不夠靈活)。
3)Redis自增
  • 為了增加ID的安全性,我們可以不直接使用Redis自增的數值,而是拼接一些其它信息:
    在這里插入圖片描述
  • 符號位:1bit,永遠為0。
  • 時間戳:31bit,以秒為單位,可以使用69年。
  • 序列號:32bit,秒內的計數器,支持每秒產生232個不同ID。

悲觀鎖

  • 悲觀鎖總是假設最壞的情況,認為共享資源每次被訪問的時候就會出現問題(比如共享數據被修改),所以每次在獲取資源操作的時候都會上鎖,這樣其他線程想拿到這個資源就會阻塞直到鎖被上一個持有者釋放。
  • 也就是說,共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉讓給其它線程。
  • 對于單機多線程來說,在 Java 中,我們通常使用 ReentrantLock 類、synchronized 關鍵字這類 JDK 自帶的本地鎖來控制一個 JVM 進程內的多個線程對本地共享資源的訪問。

樂觀鎖

  • 樂觀鎖總是假設最好的情況,認為共享資源每次被訪問的時候不會出現問題,線程可以不停地執行,無需加鎖也無需等待,只是在提交修改的時候去驗證對應的資源是否被其它線程修改了(具體方法可以使用版本號機制或 CAS 算法)。
  • 版本號機制
    • 一般是在數據表中加上一個數據版本號 version 字段,表示數據被修改的次數。
    • 當數據被修改時,version 值會+1。當線程 A 要更新數據值時,在讀取數據的同時也會讀取 version 值,在提交更新時,若剛才讀取到的 version 值為當前數據庫中的 version 值相等時才更新,否則重試更新操作,直到更新成功。
  • CAS 算法
    • CAS 的全稱是 Compare And Swap(比較與交換) ,用于實現樂觀鎖,被廣泛應用于各大框架中。CAS 的思想很簡單,就是用一個預期值和要更新的變量值進行比較,兩值相等才會進行更新。
    • CAS 是一個原子操作,底層依賴于一條 CPU 的原子指令。
    • 當多個線程同時使用 CAS 操作一個變量時,只有一個會勝出,并成功更新,其余均會失敗,但失敗的線程并不會被掛起,僅是被告知失敗,并且允許再次嘗試,當然也允許失敗的線程放棄操作。
    • 問題:
      • "ABA"問題:如果一個變量 V 初次讀取的時候是 A 值,并且在準備賦值的時候檢查到它仍然是 A 值,那我們就能說明它的值沒有被其他線程修改過了嗎?很明顯是不能的,因為在這段時間它的值可能被改為其他值,然后又改回 A,那 CAS 操作就會誤認為它從來沒有被修改過。
      • 循環時間長開銷大:CAS 經常會用到自旋操作來進行重試,也就是不成功就一直循環執行直到成功。如果長時間不成功,會給 CPU 帶來非常大的執行開銷。
      • 只能保證一個共享變量的原子操作:CAS 操作僅能對單個共享變量有效。當需要操作多個共享變量時,CAS 就顯得無能為力。

分布式鎖

  • 通過加synchronized鎖可以解決在單機情況下的一人一單等安全問題,但是在集群模式下就不行了。
  • 分布式系統下,不同的服務/客戶端通常運行在獨立的 JVM 進程上。如果多個 JVM 進程共享同一份資源的話,使用本地鎖就沒辦法實現資源的互斥訪問了。于是,分布式鎖就誕生了。
    分布式鎖

分布式鎖應滿足的需求

  • 一個最基本的分布式鎖需要滿足:
    • 互斥:任意一個時刻,鎖只能被一個線程持有。
    • 高可用:鎖服務是高可用的,當一個鎖服務出現問題,能夠自動切換到另外一個鎖服務。并且,即使客戶端的釋放鎖的代碼邏輯出現問題,鎖最終一定還是會被釋放,不會影響其他線程對共享資源的訪問。這一般是通過超時機制實現的。
    • 可重入:一個節點獲取了鎖之后,還可以再次獲取鎖。
  • 除了上面這三個基本條件之外,一個好的分布式鎖還需要滿足下面這些條件:
    • 高性能:獲取和釋放鎖的操作應該快速完成,并且不應該對整個系統的性能造成過大影響。
    • 非阻塞:如果獲取不到鎖,不能無限期等待,避免對系統正常運行造成影響。

分布式鎖實現方案

分布式鎖實現方案

基于 Redis 實現分布式鎖

  • 在 Redis 中, SETNX 命令是可以幫助我們實現互斥。SETNXSET if Not eXists (對應 Java 中的 setIfAbsent 方法),如果 key 不存在的話,才會設置 key 的值。如果 key 已經存在, SETNX 啥也不做。
  • 為了避免鎖無法被釋放,我們可以給這個 key(也就是鎖) 設置一個過期時間 。一定要保證設置指定 key 的值和過期時間是一個原子操作!!! 不然的話,依然可能會出現鎖無法被釋放的問題。
    127.0.0.1:6379> SET lockKey uniqueValue EX 3 NX
    OK
    
  • 釋放鎖的話,直接通過 DEL 命令刪除對應的 key 即可。
  • 為了防止誤刪其他的鎖,這里我們建議使用 Lua 腳本通過 key 對應的 value(唯一值)來判斷。選用 Lua 腳本是為了保證解鎖操作的原子性。因為 Redis 在執行 Lua 腳本時,可以以原子性的方式執行,從而保證了鎖釋放操作的原子性
    // 釋放鎖時,先比較鎖對應的 value 值是否相等,避免鎖的誤釋放
    if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
    elsereturn 0
    end
    
    基于 Redis 實現分布式鎖
基于 Redis 的分布式鎖存在的問題
  • 不可重入
    • 同一個線程無法多次獲取同一把鎖。
    • 解決:利用hash結構,記錄線程標示和重入次數。
  • 不可重試
    • 獲取鎖只嘗試一次就返回false,沒有重試機制。
    • 解決:利用信號量控制鎖重試
  • 超時釋放
    • 雖然可以避免死鎖,但如果業務執行耗時較長,也會導致鎖釋放,存在安全隱患。
    • 解決:Watch Dog
  • 主從一致性
    • 如果 Redis 提供了主從集群,主從同步存在延遲,當主宕機時,如果并未同步主中的鎖數據,則會出現鎖失效。
    • 解決:Redisson 的 multiLock,多個獨立的 Redis 節點,必須在所有節點都獲取重入鎖,才算獲取鎖成功。

Redisson

  • Redisson 是一個開源的 Java 語言 Redis 客戶端,提供了很多開箱即用的功能,不僅僅包括多種分布式鎖的實現。并且,Redisson 還支持 Redis 單機、Redis Sentinel、Redis Cluster 等多種部署架構。
  • Redisson 中的分布式鎖自帶自動續期機制,使用起來非常簡單,原理也比較簡單,其提供了一個專門用來監控和續期鎖的 Watch Dog( 看門狗),如果操作共享資源的線程還未執行完成的話,Watch Dog 會不斷地延長鎖的過期時間,進而保證鎖不會因為超時而被釋放。
    Redisson
  • 默認情況下,每過 10 秒,看門狗就會執行續期操作,將鎖的超時時間設置為 30 秒。看門狗續期前也會先判斷是否需要執行續期操作,需要才會執行續期,否則取消續期操作。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83648.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83648.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83648.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LabVIEW光譜信號仿真與數據處理

在光譜分析領域,LabVIEW 憑借其圖形化編程、豐富函數庫及強大數據處理能力,成為高效工具。本案例將介紹如何利用 LabVIEW 仿真光譜信號,并對實際采集的光譜數據進行處理,涵蓋信號生成、數據采集、濾波、分析及顯示等環節。 ? 一…

nginx相關面試題30道

一、基礎概念與核心特性 1. 什么是 Nginx?它的主要用途有哪些? 答案: Nginx 是一款高性能的開源 Web 服務器、反向代理服務器及負載均衡器,基于事件驅動的異步非阻塞架構,擅長處理高并發場景。 主要用途:…

數據庫實驗報告 數據定義操作 3

實驗報告(第3次) 實驗名稱 數據定義操作 實驗時間 10月12日1-2節 一、實驗內容 1、本次實驗是用sql語句創建庫和表,語句是固定的,要求熟記這些sql語句。 二、源程序及主…

霍夫圓變換全面解析(OpenCV)

文章目錄 一、霍夫圓變換基礎1.1 霍夫圓變換概述1.2 圓的數學表達與參數化 二、霍夫圓變換算法實現2.1 標準霍夫圓變換算法流程2.2 參數空間的表示與優化 三、關鍵參數解析3.1 OpenCV中的HoughCircles參數3.2 參數調優策略 四、Python與OpenCV實現參考4.1 基本實現代碼4.2 改進…

記錄一次修改nacos安全問題導致服務調用出現404

1、nacos默認值修改 nacos.core.auth.plugin.nacos.token.secret.key**** nacos.core.auth.server.identity.key******** nacos.core.auth.server.identity.value************ 重啟nacos, 這時候微服務的token認證會立即失效,等待自動重連認證或者手動重啟服務 2、…

Python面試總結

hello,大家好,我是potato,我總結一下最近的面試遇到的問題~ 1.Python開發(軟通動力) 自我介紹主要問了項目(YOLOv11)項目遇到的難點和解決方法is,列表和元組的區別Python多線程有什么問題?Pyt…

5.18 day24

知識點回顧: 元組可迭代對象os模塊 作業:對自己電腦的不同文件夾利用今天學到的知識操作下,理解下os路徑。 元組 元組的特點: 有序,可以重復,這一點和列表一樣 元組中的元素不能修改,這一點…

Uniapp中小程序調用騰訊地圖(獲取定位地址)

1、先配置權限: 這是上圖的代碼: "permission": { "scope.userLocation": { "desc": "你的位置信息將用于小程序位置接口的效果展示" } } 第二步:寫代碼: //下面是uniapp的模版代碼 主…

寫spark程序數據計算( 數據庫的計算,求和,匯總之類的)連接mysql數據庫,寫入計算結果

1. 添加依賴 在項目的 pom.xml&#xff08;Maven&#xff09;中添加以下依賴&#xff1a; xml <!-- Spark SQL --> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.12</artifactId> <version>3.3.0…

nginx服務器實驗

1.實驗要求 1&#xff09;在Nginx服務器上搭建LNMP服務&#xff0c;并且能夠對外提供Discuz論壇服務。 在Web1、Web2服務器上搭建Tomcat 服務。 2&#xff09;為nginx服務配置虛擬主機&#xff0c;新增兩個域名 www.kgc.com 和 www.benet.com&#xff0c;使用http://www.kgc.…

Spring Boot 與 RabbitMQ 的深度集成實踐(一)

引言 ** 在當今的分布式系統架構中&#xff0c;隨著業務復雜度的不斷提升以及系統規模的持續擴張&#xff0c;如何實現系統組件之間高效、可靠的通信成為了關鍵問題。消息隊列作為一種重要的中間件技術&#xff0c;應運而生并發揮著舉足輕重的作用。 消息隊列的核心價值在于其…

c++多線程debug

debug demo 命令行查看 ps -eLf|grep cam_det //查看當前運行的輕量級進程 ps -aux | grep 執行文件 //查看當前運行的進程 ps -aL | grep 執行文件 //查看當前運行的輕量級進程 pstree -p 主線程ID //查看主線程和新線程的關系 查看線程棧結構 pstack 線程ID 步驟&…

10.7 LangChain v0.3架構大升級:模塊化設計+多階段混合檢索,開發效率飆升3倍!

LangChain v0.3 技術生態與未來發展 關鍵詞:LangChain Chains, Agents 架構, Retrieval Strategy, LangGraph, 模塊化設計 3. LangChain 項目:Chains, Agents, Retrieval Strategy LangChain v0.3 通過 Chains-Agents-Retrieval 三位一體的技術棧,構建起完整的大模型應用開…

分布式 ID 生成的五種方法:優缺點與適用場景

0.簡介 在分布式系統中&#xff0c;生成全局唯一的id是一個常見的需求。由于分布式系統的特性&#xff08;多節點&#xff0c;網絡分區&#xff0c;時鐘不同步等&#xff09;&#xff0c;傳統的單機ID生成方式不再適用&#xff0c;所以一些分布式生成方式應運而生&#xff0c;…

基于單片機路燈自動控制儀仿真設計

標題:基于單片機路燈自動控制儀仿真設計 內容:1.摘要 本設計旨在解決傳統路燈控制方式效率低、能耗大的問題&#xff0c;開展了基于單片機的路燈自動控制儀仿真設計。采用單片機作為核心控制單元&#xff0c;結合光照傳感器、時鐘模塊等硬件&#xff0c;運用相關軟件進行編程和…

計算機網絡-MPLS VPN基礎概念

前面幾篇文章我們學習了MPLS的標簽轉發原理&#xff0c;有靜態標簽分發和LDP動態標簽協議&#xff0c;可以實現LSR設備基于標簽實現數據高效轉發。現在開始學習MPLS在企業實際應用的場景-MPLS VPN。 一、MPLS VPN概念 MPLS&#xff08;多協議標簽交換&#xff09;位于TCP/IP協…

LWIP的Socket接口

Socket接口簡介 類似于文件操作的一種網絡連接接口&#xff0c;通常將其稱之為“套接字”。lwIP的Socket接口兼容BSD Socket接口&#xff0c;但只實現完整Socket的部分功能 netconn是對RAW的封裝 Socket是對netconn的封裝 SOCKET結構體 struct sockaddr { u8_t sa_len; /* 長…

windows 安裝gdal實現png轉tif,以及柵格拼接

windows 安裝gdal實現png轉tif&#xff0c;以及柵格拼接 一、安裝gdal 網上有很多安裝gdal的方法&#xff0c;此處通過osgeo4w安裝gdal 1.下載osgeo4w 下載地址 https://trac.osgeo.org/osgeo4w/ 2、安裝osgeo4w exe文件安裝&#xff0c;前面部分很簡單&#xff0c;就不再…

Node.js 源碼概覽

Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行時環境&#xff0c;它的源碼結構相當龐大且復雜。下面我將為你講解 Node.js 源碼的主要結構和關鍵組成部分。 源碼結構 Node.js 的主要源碼目錄結構如下&#xff1a; node/ ├── lib/ # JavaScript 核心模…

Linux :線程 【生產者消費者模型】

Linux &#xff1a;線程 【生產者消費者模型與信號量】 &#xff08;一&#xff09;生產消費模型1、生產消費模式概念2、生產者消費者之間的關系3、生產者消費者模型優點 &#xff08;二&#xff09;基于BlockingQueue的生產者消費者模型1、基于阻塞隊列模型2、模擬實現基于阻塞…