日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

分布式锁之Redis6+Lua脚本实现原生分布式锁

發布時間:2023/12/3 数据库 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式锁之Redis6+Lua脚本实现原生分布式锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

        • 簡介
        • 設計分布式鎖應該考慮的東?
        • 基于Redis實現分布式鎖
        • 總結
        • 解決解鎖的原子性
        • 代碼實現
        • 遺留?個問題

簡介

分布式鎖核?知識介紹和注意事項

背景

就是保證同?時間只有?個客戶端可以對共享資源進?操作

案例

優惠券領劵限制張數、商品庫存超賣

核?

為了防?分布式系統中的多個進程之間相互?擾,我們需要?種分布式協調技術來對這些進程進?調度利?互斥機制來控制共享資源的訪問,這就是分布式鎖要解決的問題避免共享資源并發操作導致數據問題

加鎖

本地鎖:synchronize、lock等,鎖在當前進程內,集群部署下依舊存在問題
分布式鎖:redis、zookeeper等實現,雖然還是鎖,但是多個進程共?的鎖標記,可以?Redis、Zookeeper、Mysql等都可以

設計分布式鎖應該考慮的東?

1.排他性 --在分布式應?集群中,同?個?法在同?時間只能被?臺機器上的?個線程執?
2.容錯性 --分布式鎖?定能得到釋放,?如客戶端奔潰或者?絡中斷
3.滿?可重?、?性能、?可?
4.注意分布式鎖的開銷、鎖粒度

基于Redis實現分布式鎖

實現分布式鎖 可以? Redis、Zookeeper、Mysql數據庫這?種 , 性能最好的是Redis

分布式鎖離不開 key - value 設置
key 是鎖的唯?標識,?般按業務來決定命名,?如想要給?種優惠券活動加鎖,key 命名為 “coupon:id” 。value就可以使?固定值,?如設置成1

加鎖 SETNX key value
解鎖 del (key)
配置鎖超時 expire (key,30s)

綜合偽代碼

1.setnx 的含義就是 SET if Not Exists,有兩個參數setnx(key, value),該?法是原?性操作
2.如果 key 不存在,則設置當前 key 成功,返回 1
3.如果當前 key 已經存在,則設置當前 key 失敗,返回 0
4.得到鎖的線程執?完任務,需要釋放鎖,以便其他線程可以進?,調? del(key)
5.客戶端奔潰或者?絡中斷,資源將會永遠被鎖住,即死鎖,因此需要給key配置過期時間,以保證即使沒有被顯式釋放,這把鎖也要在?定時間后?動釋放

methodA(){String key = "coupon_66";if(setnx(key,1== 1{expire(key,30,TimeUnit.MILLISECONDS)try {//做對應的業務邏輯//查詢?戶是否已經領券//如果沒有則扣減庫存//新增領劵記錄} finally {del(key)}}else{//睡眠100毫秒,然后?旋調?本?法methodA()} }

存在什么問題?

多個命令之間不是原?性操作,如setnx和expire之間,如果setnx成功,但是expire失敗,且宕機了,則這個資源就是死鎖業務超時,存在其他線程勿刪,key 30秒過期,假如線程A執?很慢超過30秒,則key就被釋放了,其他線程B就得到了鎖,這個時候線程A執?完成,?B還沒執?完成,結果就是線程A刪除了線程B加的鎖

問題解決

使?原?命令:設置和配置過期時間 setnx / setex
如: set key 1 ex 30 nx
java??redisTemplate.opsForValue().setIfAbsent(“seckill_1”,“success”,30,TimeUnit.MILLISECONDS)
可以在 del 釋放鎖之前做?個判斷,驗證當前的鎖是不是??加的鎖, 那 value 應該是存當前線程的標識或者uuid
String key = "coupon_66"String value = Thread.currentThread().getId()
進?步細化誤刪當線程A獲取到正常值時,返回帶代碼中判斷期間鎖過期了,線程B剛好重新設置了新值,線程A那邊有判斷value是??的標識,然后調?del?法,結果就是刪除了新設置的線程B的值核?還是判斷和刪除命令不是原?性操作導致

總結

加鎖+配置過期時間:保證原?性操作
解鎖: 防?誤刪除、也要保證原?性操作

解決解鎖的原子性

前?說了redis做分布式鎖存在的問題核?是保證多個指令原?性,加鎖使?setnx setex 可以保證原?性,那解鎖使? 判斷和刪除怎么保證原?性

多個命令的原?性:采? lua腳本+redis, 由于【判斷和刪除】是lua腳本執?,所以要么全成功,要么全失敗

//獲取lock的值和傳遞的值?樣,調?刪除操作返回1,否則返回0 String script = "if redis.call('get',KEYS[1])== ARGV[1] then returnredis.call('del',KEYS[1]) else return 0 end"; //Arrays.asList(lockKey)是key列表,uuid是參數 Integer result = redisTemplate.execute(newDefaultRedisScript<>(script, Integer.class),Arrays.asList(lockKey), uuid);

代碼實現

import net.xdclass.xdclassredis.util.JsonData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;import java.time.Duration; import java.util.Arrays; import java.util.Random; import java.util.UUID; import java.util.concurrent.TimeUnit;@RestController @RequestMapping("/api/v1/coupon") public class CouponController {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@GetMapping("add")public JsonData saveCoupon(@RequestParam(value = "coupon_id",required = true) int couponId){//防止其他線程誤刪String uuid = UUID.randomUUID().toString();String lockKey = "lock:coupon:"+couponId;lock(couponId,uuid,lockKey);return JsonData.buildSuccess();}private void lock(int couponId,String uuid,String lockKey){//lua腳本String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";Boolean nativeLock = redisTemplate.opsForValue().setIfAbsent(lockKey,uuid,Duration.ofSeconds(30));System.out.println(uuid+"加鎖狀態:"+nativeLock);if(nativeLock){//加鎖成功try{//TODO 做相關業務邏輯TimeUnit.SECONDS.sleep(10L);} catch (InterruptedException e) {} finally {//解鎖Long result = redisTemplate.execute( new DefaultRedisScript<>(script,Long.class),Arrays.asList(lockKey),uuid);System.out.println("解鎖狀態:"+result);}}else {//自旋操作try {System.out.println("加鎖失敗,睡眠5秒 進行自旋");TimeUnit.MILLISECONDS.sleep(5000);} catch (InterruptedException e) { }//睡眠一會再嘗試獲取鎖lock(couponId,uuid,lockKey);}}}

遺留?個問題

鎖的過期時間,如何實現鎖的?動續期或者避免業務執?時間過?,鎖過期了?

1.原??式的話,?般把鎖的過期時間設置久?點,?如10分鐘時間
原?代碼+redis實現分布式鎖使??較復雜,且有些鎖續期問題更難處理

2.框架 官?推薦?式:https://redis.io/topics/distlock 使?特別簡單

總結

以上是生活随笔為你收集整理的分布式锁之Redis6+Lua脚本实现原生分布式锁的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。