Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to implement distributed Lock with Go+Redis

2025-01-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces how to use Go+Redis to achieve distributed locks, the article is very detailed, has a certain reference value, interested friends must read it!

Why do you need distributed locks?

The user places an order

Lock the uid to prevent duplicate orders.

Inventory deduction

Lock up the inventory to prevent overselling.

Balance deduction

Lock the account to prevent concurrent operations.

When sharing the same resource in a distributed system, distributed locks are often needed to ensure the consistency of changed resources.

Distributed locks need to have characteristics

Exclusiveness

The basic characteristics of the lock and can only be held by the first holder.

Anti-deadlock

Once a deadlock occurs in critical resources in high concurrency scenarios, it is very difficult to troubleshoot, which can usually be avoided by setting the timeout to automatically release the lock when the timeout expires.

Reentrant

The lock holder supports reentrant to prevent the lock from being released during the timeout when the lock holder reenters again.

High performance and high availability

The lock is the key pre-node for the code to run, and once it is not available, the business will report the failure directly. In high concurrency scenarios, high performance and high availability is a basic requirement.

What knowledge points should be mastered first to realize Redis lock

Set command

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

EX second: sets the expiration time of the key to second seconds. The SET key value EX second effect is equivalent to SETEX key second value.

PX millisecond: sets the expiration time of the key to millisecond milliseconds. The SET key value PX millisecond effect is equivalent to PSETEX key millisecond value.

NX: set the key only when the key does not exist. The SET key value NX effect is equivalent to SETNX key value.

XX: set the key only if it already exists.

Redis.lua script

Using redis lua script, a series of command operations can be encapsulated into pipline to realize the atomicity of the whole operation.

Analysis of RedisLock Source Code of go-zero distributed Lock

Core/stores/redis/redislock.go

Locking process

-- KEYS [1]: lock key-- ARGV [1]: lock value, random string-- ARGV [2]: expiration time-- determine whether the value held by the lock key is equal to the passed value--. If equality means that the lock is acquired again and the acquisition time is updated. Prevent expiration on reentry-here it is "reentrant lock" if redis.call ("GET", KEYS [1]) = = ARGV [1] then-set redis.call ("SET", KEYS [1], ARGV [1], "PX") ARGV [2]) return "OK" else-if the lock key.value is not equal to the passed value, it means that this is the first time to acquire the lock-- SET key value NX PX timeout: set the value of key when the key does not exist-- "OK" will be returned automatically if the setting is successful. Failed setting returned "NULL Bulk Reply"-Why do you add "NX" here, because you need to prevent other people's locks from overwriting return redis.call ("SET", KEYS [1], ARGV [1], "NX", "PX", ARGV [2]) end

Unlock process

-- release locks-- cannot release other people's locks if redis.call ("GET", KEYS [1]) = = ARGV [1] then-- return "1" return redis.call ("DEL", KEYS [1]) else return 0end after successful execution

Source code parsing

Package redisimport ("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 0end` randomLen = 16 / default timeout Prevent deadlock tolerance = 500 / / milliseconds millisPerSecond = 1000) / / A RedisLock is a redis lock.type RedisLock struct {/ / redis client store * Redis / / timeout seconds uint32 / / lock key key string / / lock value Prevent locks from being acquired by 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, / / when acquiring locks Lock values are generated through random strings / / in fact go-zero provides a more efficient way to generate random strings / / see core/stringx/random.go:Randn id: randomStr (randomLen),}} / / Acquire acquires the lock.// locking func (rl * RedisLock) Acquire () (bool Error) {/ / get expiration time seconds: = atomic.LoadUint32 (& rl.seconds) / / default lock expiration time is 500ms Prevent deadlock 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.// release 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.// needs to be called before Acquire (). The default is that 500ms automatically releases 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)} above are all the contents of the article "how to implement distributed locks with Go+Redis" Thank you for reading! Hope to share the content to help you, more related knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report