基于redis實現分布式鎖的原理與方法_Redis

來源:腳本之家  責任編輯:小易  

分布式鎖的三種實現方式分別是:基于數據庫實現分布式鎖、基于緩存(Redis等)實現分布式鎖、基于Zookeeper實現分布式鎖。一、基于數據庫實現分布式鎖1、悲觀鎖利用select…where…for update 排他鎖。注意:其他附加功能與實現一基本一致,這里需要注意的是“where name=lock”,name字段必須要走索引,否則會鎖表。有些情況下,比如表不大,mysql優化器會不走這個索引,導致鎖表問題。2、樂觀鎖所謂樂觀鎖與前邊最大區別在于基于CAS思想,是不具有互斥性,不會產生鎖等待而消耗資源,操作過程中認為不存在并發沖突,只有update version失敗后才能覺察到,搶購和秒殺就是用了這種實現以防止超賣,通過增加遞增的版本號字段實現樂觀鎖。二、基于緩存(Redis等)實現分布式鎖1、使用命令介紹:(1)SETNXSETNX key val:當且僅當key不存在時,set一個key為val的字符串,返回1;若key存在,則什么都不做,返回0。(2)expireexpire key timeout:為key設置一個超時時間,單位為second,超過這個時間鎖會自動釋放,避免死鎖。(3)deletedelete key:刪除key在使用Redis實現分布式鎖的時候,主要就會使用到這三個命令。2、實現思想:(1)獲取鎖的時候,使用setnx加鎖,并使用expire命令為鎖添加一個超時時間,超過該時間則自動釋放鎖,鎖的value值為一個隨機生成的UUID,通過此在釋放鎖的時候進行判斷。(2)獲取鎖的時候還設置一個獲取的超時時間,若超過這個時間則放棄獲取鎖。(3)釋放鎖的時候,通過UUID判斷是不是該鎖,若是該鎖,則執行delete進行鎖釋放。三、基于Zookeeper實現分布式鎖ZooKeeper是一個為分布式應用提供一致性服務的開源組件,它內部是一個分層的文件系統目錄樹結構,規定同一個目錄下只能有一個唯一文件名;赯ooKeeper實現分布式鎖的步驟如下:(1)創建一個目錄mylock。(2)線程A想獲取鎖就在mylock目錄下創建臨時順序節點。(3)獲取mylock目錄下所有的子節點,然后獲取比自己小的兄弟節點,如果不存在,則說明當前線程順序號最小,獲得鎖。(4)線程B獲取所有節點,判斷自己不是最小節點,設置監聽比自己次小的節點。(5)線程A處理完,刪除自己的節點,線程B監聽到變更事件,判斷自己是不是最小的節點,如果是則獲得鎖。擴展資料;一、數據庫分布式鎖實現的缺點:1、db操作性能較差,并且有鎖表的風險。2、非阻塞操作失敗后,需要輪詢,占用cpu資源。3、長時間不commit或者長時間輪詢,可能會占用較多連接資源。二、Redis(緩存)分布式鎖實現的缺點:1、鎖刪除失敗,過期時間不好控制。2、非阻塞,操作失敗后,需要輪詢,占用cpu資源。三、ZK分布式鎖實現的缺點:性能不如redis實現,主要原因是寫操作(獲取鎖釋放鎖)都需要在Leader上執行,然后同步到followerwww.anxorj.tw防采集請勿采集本網。

前言

系統的不斷擴大,分布式鎖是最基本的保障。與單機的多線程不一樣的是,分布式跨多個機器。線程的共享變量無法跨機器。

使用Redis實現分布式鎖最簡單的方案是使用命令SETNX。SETNX(SET if Not eXist)的使用方式為:SETNX key value,只在鍵key不存在的情況下,將鍵key的值設置為value,若鍵key存在,則SETNX不做

為了保證一個在高并發存場景下只能被同一個線程操作,java并發處理提供ReentrantLock或Synchronized進行互斥控制。但是這僅僅對單機環境有效。我們實現分布式鎖大概通過三種方式。 redis實現分布式鎖 數據庫實現分布式鎖 zk實現分布式鎖

比如:秒殺,全局遞增ID,樓層生成等等。大部分的解決方案是基于DB實現的,Redis為單進程單線程模式,采用隊列模式將并發訪問其次Redis提供一些命令SETNX,GETSET,可以方便實現分布式鎖機制。

memcached帶有add函數,利用add函數的特性即可實現分布式鎖。add和set的區別在于:如果多線程并發set,則每個set都會成功,但最后存儲的值以最后的set的線程為準。而add的話則相反,add會添加第一個到達的

今天我們介紹通過redis實現分布式鎖。實際上這三種和java對比看屬于一類。都是屬于程序外部鎖。

利用上面這兩個特性,我們來看下獲取實現分布式鎖的基本邏輯: 客戶端調用create()方法創建名為“locknode/guid-lock-”的節點,需要注意的是,這里節點的創建類型需要設置為EPHEMERAL_SEQUENTIAL?蛻舳

原理剖析 上述三種分布式鎖都是通過各自為依據對各個請求進行上鎖,解鎖從而控制放行還是拒絕。redis鎖是基于其提供的setnx命令。 setnx當且僅當key不存在。若給定key已經存在,則setnx不做任何動作。setnx是一個原子性操作。 和數據庫分布式相比,因為redis內存輕量。所以redis分布式鎖性能更好

基于配置 (nodes-port.conf) 的集群管理 6):ASK 轉向 MOVED 轉向機制 2:redis cluster 架構 1)redis-cluster 架構圖 架構細節 (1) 所有的 redis 節點彼此互聯 (PING-PONG 機制 內部使用二進制協議優化傳輸

memcached帶有add函數,利用add函數的特性即可實現分布式鎖。add和set的區別在于:如果多線程并發set,則每個set都會成功,但最后存儲的值以最后的set的線程為準。而add的話則相反,add會添加第一個到達的

實現

原理很簡單。結合springboot項目我們實現一套通過注解形式對接口進行庫存上鎖案例進行理解

編寫注解

我們編寫注解。方便我們在接口上添加注解提供攔截信息

/** * @author 張新華 * @version V1.0 * @Package com.ay.framework.order.redis.product * @date 2020年03月26日, 0026 10:29 * @Copyright © 2020 安元科技有限公司 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@[email protected] @interface StockLock { /** * @author zxhtom * @Description 鎖key的前綴 * @Date 15:25 2020年03月25日, 0025 * @Param [] * @return java.lang.String */ String prefix() default ""; /** * @author zxhtom * @Description key的分隔符 * @Date 15:27 2020年03月25日, 0025 * @Param [] * @return java.lang.String */ String delimiter() default ":";}

/** * @author 張新華 * @version V1.0 * @Package com.ay.framework.order.redis.product * @date 2020年03月26日, 0026 11:09 * @Copyright © 2020 安元科技有限公司 */@Target({ElementType.PARAMETER , ElementType.METHOD , ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@[email protected] @interface StockParam { /** * @author zxhtom * @Description 組成key * @Date 11:11 2020年03月26日, 0026 * @Param [] * @return java.lang.String[] */ String[] names() default {""};}

攔截器攔截

redis分布式鎖實現的關鍵就是攔截器的編寫。上面的注解只是為了實現攔截的一個輔助。

@Around("execution(public * *(..)) && @annotation(com.ay.framework.order.redis.product.StockLock)")

通過springboot的Around進行針對StockLock注解的攔截。通過攔截我們可以獲取到攔截的方法、參數、及需要的鎖的參數。

我們獲取到需要鎖的名稱這里叫做【a】之后通過redis的原子性操作對該key進行遞減操作。

為了方便我們在削減庫存的時候可以對庫存進行更新操作。我們在遞減庫存前還需要借助于另一把鎖。 這一把鎖我們叫做【a_key】

換句話說我們接口想訪問就必須獲取【a】鎖,拿到【a】鎖需要減少庫存。減少庫存之前需要獲取【a_key】鎖。

拿到鎖之后處理完邏輯之后我們需要釋放對應鎖。

RedisAtomicLong entityIdCounter = new RedisAtomicLong(lockKey, redisTemplate.getConnectionFactory()); if (redisTemplate.hasKey(CoreConstants.UPDATEPRODUCTREDISLOCKKEY + lockKey)) { //表示lockKey的庫存信息有變動。此時無法進行交易 throw new BusinessException("庫存變動。暫無法交易"); } Long increment = entityIdCounter.decrementAndGet(); if (increment >= 0) { try { Object proceed = pjp.proceed(); } catch (Throwable throwable) { //所占資源需要釋放回資源池 while (!redisLock.tryGetLock(CoreConstants.UPDATEPRODUCTREDISLOCKKEY + lockKey, "")) { } //表示lockKey的庫存信息有變動。此時無法進行交易 long l = entityIdCounter.incrementAndGet(); if (l < 1) { redisTemplate.opsForValue().set(lockKey,1); } redisLock.unLock(CoreConstants.UPDATEPRODUCTREDISLOCKKEY + lockKey); throwable.printStackTrace(); } } else { redisTemplate.opsForValue().set(lockKey,0); throw new BusinessException("庫存不足!無法操作"); }

因為我們上鎖就需要釋放鎖。但是程序在中途處理業務是發生異常導致沒有走到釋放鎖的步驟。這個時候就導致我們的分布式鎖一直被鎖。俗稱【死鎖】。為了避免這種場景的發生。我們常常在上鎖的時候給一個有效期。有效期已過自動釋放鎖。這個特性恰好和redis的過期策略不摩爾和。

上述提及工具

RedisLock

public Boolean tryGetLock(String key , String value) { return tryGetLock(key, value, -1, TimeUnit.DAYS);}public Boolean tryGetLock(String key , String value, Integer expire) { return tryGetLock(key, value, expire, TimeUnit.SECONDS);}public Boolean tryGetLock(String key , String value, Integer expire , TimeUnit timeUnit) { ValueOperations operations = redisTemplate.opsForValue(); if (operations.setIfAbsent(key, value)) { //說明 redis沒有該key , 換言之 加鎖成功 設置過期時間防止死鎖 if (expire > 0) { redisTemplate.expire(key, expire, timeUnit); } return true; } return false;}public Boolean unLock(String key) { return redisTemplate.delete(key);}

StockKeyGenerator

@Component()@Primarypublic class StockKeyGenerator implements CacheKeyGenerator { @Override public String getLockKey(ProceedingJoinPoint pjp) { //獲取方法簽名 MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); //獲取方法cacheLock注解 StockLock stockLock = method.getAnnotation(StockLock.class); //獲取方法參數 Object[] args = pjp.getArgs(); Parameter[] parameters = method.getParameters(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < parameters.length; i++) { StockParam stockParam = parameters[i].getAnnotation(StockParam.class); Object arg = args[i]; if (arg instanceof Map) { Map<String, Object> temArgMap = (Map<String, Object>) arg; String[] names = stockParam.names(); for (String name : names) { if (builder.length() > 0) { builder.append(stockLock.delimiter()); } builder.append(temArgMap.get(name)); } } } return builder.toString(); }}

問題分析

上面分析了一個死鎖的場景,理論上出了死鎖我們redis分布鎖很好的解決了分布式問題。但是還是會出現問題。下面列舉寫小編遇到的問題。

業務處理時間>上鎖過期時間

a線程獲取到鎖,開始進行業務處理需要8S,

在8S內,鎖的有效期是5S,在鎖過期后也就是第6S , b線程進入開始獲取鎖這個時候b是可以獲取到新鎖的。這個時候就是有問題的。

假設b線程業務處理只需要3S , 但是因為a線程釋放了鎖,所以在第8S的時候雖然b線程沒有釋放鎖,b的鎖也沒有過期但是這時候也沒有了鎖。從而導致C線程也可以進入

總結

到此這篇基于redis實現分布式鎖的原理與方法的文章就介紹到這了,更多相關redis分布式鎖內容請搜索真格學網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持真格學網!

Redis分布式bai鎖的安全性問題,在分布式系統專家和Redis的作者 antirez 之間就發生過一場爭論。由于對這個問題一du直以來比zhi較關注,所以我前些日子仔細閱讀了與這場爭論相關的資料。這dao場爭論的大概過程是這樣的:為了規范專各家對基于Redis的分布式鎖的實現,屬Redis的作者提出了一個更安全的實現,叫做 Redlock ,被父母贈與的內容來自www.anxorj.tw請勿采集。


  • 本文相關:
  • redis中使用java腳本實現分布式鎖
  • redis實現分布式鎖的幾種方法總結
  • 基于redis實現分布式鎖以及任務隊列
  • 詳解java如何實現基于redis的分布式鎖
  • 詳解使用redis setnx 命令實現分布式鎖
  • 淺談redis分布式鎖的正確實現方式
  • java基于jedislock—redis分布式鎖實現示例代碼
  • redis上實現分布式鎖以提高性能的方案研究
  • springboot集成redisson實現分布式鎖的方法示例
  • java使用redisson分布式鎖實現原理
  • redis教程(七):key操作命令詳解
  • redis通過pipeline提升吞吐量的方法
  • 詳解redis端口號
  • redis中3種特殊的數據類型(bitmap、geo和hyperloglog)
  • redis上實現分布式鎖以提高性能的方案研究
  • redis string 類型和 hash 類型學習筆記與總結
  • redis集群的相關詳解
  • 詳解redis中的雙鏈表結構
  • windows下redis的安裝使用教程
  • redis優化經驗總結(必看篇)
  • 什么是分布式鎖及正確使用redis實現分布式鎖
  • 分布式鎖的三種實現方式
  • 基于Redis的分布式鎖真的安全嗎
  • 基于Redis的分布式鎖到底安全嗎
  • 大家所推崇的 Redis 分布式鎖,真的可以萬無一失嗎?
  • redis 為什么需要分布式鎖
  • java trylock能實現分布式鎖嗎
  • 如何用Redlock實現分布式鎖
  • 如何使用 redis 集群來實現分布式儲存
  • 為何Redis用樂觀鎖,而MySQL數據庫卻沒有
  • 網站首頁網頁制作腳本下載服務器操作系統網站運營平面設計媒體動畫電腦基礎硬件教程網絡安全mssqlmysqlmariadboracledb2mssql2008mssql2005sqlitepostgresqlmongodbredisaccess數據庫文摘數據庫其它首頁redis中使用java腳本實現分布式鎖redis實現分布式鎖的幾種方法總結基于redis實現分布式鎖以及任務隊列詳解java如何實現基于redis的分布式鎖詳解使用redis setnx 命令實現分布式鎖淺談redis分布式鎖的正確實現方式java基于jedislock—redis分布式鎖實現示例代碼redis上實現分布式鎖以提高性能的方案研究springboot集成redisson實現分布式鎖的方法示例java使用redisson分布式鎖實現原理redis教程(七):key操作命令詳解redis通過pipeline提升吞吐量的方法詳解redis端口號redis中3種特殊的數據類型(bitmap、geo和hyperloglog)redis上實現分布式鎖以提高性能的方案研究redis string 類型和 hash 類型學習筆記與總結redis集群的相關詳解詳解redis中的雙鏈表結構windows下redis的安裝使用教程redis優化經驗總結(必看篇)超強、超詳細redis數據庫入門教程redis常用命令、常見錯誤、配置技redis操作命令總結redis中5種數據結構的使用場景介64位windows下安裝redis教程redis中使用redis-dump導出、導入redis中統計各種數據大小的方法redis常用命令小結讓redis在你的系統中發揮更大作用centos 6.6下redis安裝配置記錄redis 替代php文件存儲session的實例redis設置密碼保護的實例講解基于redis實現的點贊功能設計思路詳解redis事務常用操作詳解redis連接錯誤的情況總結分析redis中的數據過期策略詳解詳解ssh框架和redis的整合redis sentinel實現高可用配置的詳細步驟redis教程(十四):內存優化介紹windows下redis安裝配置教程
    免責聲明 - 關于我們 - 聯系我們 - 廣告聯系 - 友情鏈接 - 幫助中心 - 頻道導航
    Copyright © 2017 www.anxorj.tw All Rights Reserved
    陕西快乐10分下载