1. 用 Go + Redis 實現(xiàn)分布式鎖

        共 4256字,需瀏覽 9分鐘

         ·

        2021-12-18 14:12

        為什么需要分布式鎖

        1. 用戶下單

        鎖住 uid,防止重復(fù)下單。

        1. 庫存扣減

        鎖住庫存,防止超賣。

        1. 余額扣減

        鎖住賬戶,防止并發(fā)操作。分布式系統(tǒng)中共享同一個資源時往往需要分布式鎖來保證變更資源一致性。

        分布式鎖需要具備特性

        1. 排他性

        鎖的基本特性,并且只能被第一個持有者持有。

        1. 防死鎖

        高并發(fā)場景下臨界資源一旦發(fā)生死鎖非常難以排查,通??梢酝ㄟ^設(shè)置超時時間到期自動釋放鎖來規(guī)避。

        1. 可重入

        鎖持有者支持可重入,防止鎖持有者再次重入時鎖被超時釋放。

        1. 高性能高可用

        鎖是代碼運行的關(guān)鍵前置節(jié)點,一旦不可用則業(yè)務(wù)直接就報故障了。高并發(fā)場景下,高性能高可用是基本要求。

        實現(xiàn) Redis 鎖應(yīng)先掌握哪些知識點

        1. set 命令

        SET key value [EX seconds] [PX milliseconds] [NX|XX]

        • EXsecond :設(shè)置鍵的過期時間為 second 秒。SET key value EX second 效果等同于 SETEX key second value 。
        • PXmillisecond :設(shè)置鍵的過期時間為 millisecond 毫秒。SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
        • NX:只在鍵不存在時,才對鍵進(jìn)行設(shè)置操作。SET key value NX 效果等同于 SETNX key value 。
        • XX:只在鍵已經(jīng)存在時,才對鍵進(jìn)行設(shè)置操作。
        1. Redis.lua 腳本

        使用 redis lua 腳本能將一系列命令操作封裝成 pipline 實現(xiàn)整體操作的原子性。

        go-zero 分布式鎖 RedisLock 源碼分析

        core/stores/redis/redislock.go

        1. 加鎖流程
        --?KEYS[1]:?鎖key
        --?ARGV[1]:?鎖value,隨機(jī)字符串
        --?ARGV[2]:?過期時間
        --?判斷鎖key持有的value是否等于傳入的value
        --?如果相等說明是再次獲取鎖并更新獲取時間,防止重入時過期
        --?這里說明是“可重入鎖”
        if?redis.call("GET",?KEYS[1])?==?ARGV[1]?then
        ????--?設(shè)置
        ????redis.call("SET",?KEYS[1],?ARGV[1],?"PX",?ARGV[2])
        ????return?"OK"

        else
        ????--?鎖key.value不等于傳入的value則說明是第一次獲取鎖
        ????--?SET?key?value?NX?PX?timeout?:?當(dāng)key不存在時才設(shè)置key的值
        ????--?設(shè)置成功會自動返回“OK”,設(shè)置失敗返回“NULL?Bulk?Reply”
        ????--?為什么這里要加“NX”呢,因為需要防止把別人的鎖給覆蓋了
        ????return?redis.call("SET",?KEYS[1],?ARGV[1],?"NX",?"PX",?ARGV[2])
        end
        1. 解鎖流程
        --?釋放鎖
        --?不可以釋放別人的鎖
        if?redis.call("GET",?KEYS[1])?==?ARGV[1]?then
        ????--?執(zhí)行成功返回“1”
        ????return?redis.call("DEL",?KEYS[1])
        else
        ????return?0
        end
        1. 源碼解析
        package?redis

        import?(
        ????"math/rand"
        ????"strconv"
        ????"sync/atomic"
        ????"time"

        ????red?"github.com/go-redis/redis"
        ????"github.com/tal-tech/go-zero/core/logx"
        )

        const?(
        ????letters?????=?"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        ????lockCommand?=?`if?redis.call("GET",?KEYS[1])?==?ARGV[1]?then
        ????redis.call("SET",?KEYS[1],?ARGV[1],?"PX",?ARGV[2])
        ????return?"OK"
        else
        ????return?redis.call("SET",?KEYS[1],?ARGV[1],?"NX",?"PX",?ARGV[2])
        end`

        ????delCommand?=?`if?redis.call("GET",?KEYS[1])?==?ARGV[1]?then
        ????return?redis.call("DEL",?KEYS[1])
        else
        ????return?0
        end`

        ????randomLen?=?16
        ????//?默認(rèn)超時時間,防止死鎖
        ????tolerance???????=?500?//?milliseconds
        ????millisPerSecond?=?1000
        )

        //?A?RedisLock?is?a?redis?lock.
        type?RedisLock?struct?{
        ????//?redis客戶端
        ????store?*Redis
        ????//?超時時間
        ????seconds?uint32
        ????//?鎖key
        ????key?string
        ????//?鎖value,防止鎖被別人獲取到
        ????id?string
        }

        func?init()?{
        ????rand.Seed(time.Now().UnixNano())
        }

        //?NewRedisLock?returns?a?RedisLock.
        func?NewRedisLock(store?*Redis,?key?string)?*RedisLock?{
        ????return?&RedisLock{
        ????????store:?store,
        ????????key:???key,
        ????????//?獲取鎖時,鎖的值通過隨機(jī)字符串生成
        ????????//?實際上go-zero提供更加高效的隨機(jī)字符串生成方式
        ????????//?見core/stringx/random.go:Randn
        ????????id:????randomStr(randomLen),
        ????}
        }

        //?Acquire?acquires?the?lock.
        //?加鎖
        func?(rl?*RedisLock)?Acquire()?(bool,?error)?{
        ????//?獲取過期時間
        ????seconds?:=?atomic.LoadUint32(&rl.seconds)
        ????//?默認(rèn)鎖過期時間為500ms,防止死鎖
        ????resp,?err?:=?rl.store.Eval(lockCommand,?[]string{rl.key},?[]string{
        ????????rl.id,?strconv.Itoa(int(seconds)*millisPerSecond?+?tolerance),
        ????})
        ????if?err?==?red.Nil?{
        ????????return?false,?nil
        ????}?else?if?err?!=?nil?{
        ????????logx.Errorf("Error?on?acquiring?lock?for?%s,?%s",?rl.key,?err.Error())
        ????????return?false,?err
        ????}?else?if?resp?==?nil?{
        ????????return?false,?nil
        ????}

        ????reply,?ok?:=?resp.(string)
        ????if?ok?&&?reply?==?"OK"?{
        ????????return?true,?nil
        ????}

        ????logx.Errorf("Unknown?reply?when?acquiring?lock?for?%s:?%v",?rl.key,?resp)
        ????return?false,?nil
        }

        //?Release?releases?the?lock.
        //?釋放鎖
        func?(rl?*RedisLock)?Release()?(bool,?error)?{
        ????resp,?err?:=?rl.store.Eval(delCommand,?[]string{rl.key},?[]string{rl.id})
        ????if?err?!=?nil?{
        ????????return?false,?err
        ????}

        ????reply,?ok?:=?resp.(int64)
        ????if?!ok?{
        ????????return?false,?nil
        ????}

        ????return?reply?==?1,?nil
        }

        //?SetExpire?sets?the?expire.
        //?需要注意的是需要在Acquire()之前調(diào)用
        //?不然默認(rèn)為500ms自動釋放
        func?(rl?*RedisLock)?SetExpire(seconds?int)?{
        ????atomic.StoreUint32(&rl.seconds,?uint32(seconds))
        }

        func?randomStr(n?int)?string?{
        ????b?:=?make([]byte,?n)
        ????for?i?:=?range?b?{
        ????????b[i]?=?letters[rand.Intn(len(letters))]
        ????}
        ????return?string(b)
        }

        關(guān)于分布式鎖還有哪些實現(xiàn)方案

        1. etcd
        2. redis redlock

        項目地址

        https://github.com/zeromicro/go-zero

        歡迎使用 go-zerostar 支持我們!



        推薦閱讀


        福利

        我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號 「polarisxu」,回復(fù)?ebook?獲?。贿€可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

        瀏覽 45
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 91麻豆精品国产91 | xxxxx无码 | 72式性无遮挡免费视频看片 | 美女视频黄特级大片 | 免费观看成人 |