golang使用Redsync分布式锁


一、什么是锁?

  1. 锁: 是一种同步机制,用于在多个执行线程的环境中强制对资源的访问进行限制。
  2. 分布式锁: 在编程语言中就是一个变量,该变量在同一时刻只能有一个线程拥有,以便保护共享数据在同一时刻只有一个线程去操作。而分布式锁也是锁,即分布式系统中的锁。该锁是用于解决在分布式系统中控制共享资源访问的问题的。
  3. 常见的使用场景:
    • 库存扣减,防止超卖
    • 数据幂等性,防止表单重复提交
  4. 分布式锁主要特征:
    • 互斥:同一时刻只能有一个线程获得锁。
    • 可重入:当一个线程获取锁后,还可以再次获取这个锁,避免死锁发生。
    • 高可用:当小部分节点挂掉后,仍然能够对外提供服务。
    • 高性能:要做到高并发、低延迟。

二、分布式锁实现方式

基于数据库 (并发较小)

  1. selelct … for update 悲观锁实现分布式锁
    • 基于数据库的悲观锁实现分布式锁 select … for update
    • MySQL中通过select…for update实现悲观锁
    • select…for update仅适用于InnoDB,且查询需命中,事务需在手动提交下才能生效
  2. 乐观锁
    • 数据表中增加version字段,修改字段时判断版本和历史版本是否一致如果一致可以直接修改,如果不一致则进行重新请求 可以使用while

基于Redis实现分布式锁(setnx+expire)

设置锁,设置过期时间

# setnx key value
set lock_1 1
# expire  key time 单位秒
expire lock_1 10

逻辑执行完毕手动操作释放锁

# 释放锁
del key 

上面那种实现方式存在一种问题,如果在执行完 setnx 之后,执行expire之前,服务器 crash 或重启了导致加的这个锁没有设置过期时间,就会导致死锁的情况(别的线程就永远获取不到锁了)

为了解决上述问题我们可以使用一个命令来设置锁并设置过期时间

#set key ex 10 nx 设置锁和设置过期时间一个命令完成
set lock_1 ex 10 nx

上面的方式可能存在新的问题,如果业务没有执行完毕,因为过期时间导致的了锁被释放。这样就失去了锁的安全性,有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实问题的关键就在于我们不确定要设置多长时间,时间太短就会导致锁过期释放,业务没执行完,时间太长就会使系统运行效率下降. 我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。

我么可以使用看门口 watch_dog,当我面的业务代码执行时我们设置一个线程,执行循环操作,循环时间大致为锁过期时间的一半,如果锁还存在,那么我们重新设置过期时间,当我们的业务逻辑代码执行完毕之后,关闭这个线程。

3.golang使用redsync

  1. 安装redis
    # 拉取镜像
    docker pull redis
    # 启动
    docker run -p 6379:6379 --name myredis -d redis
    
  2. golang代码说明
    文档地址
    package main
    
    import (
    	goredislib "github.com/redis/go-redis/v9"
    	"github.com/go-redsync/redsync/v4"
    	"github.com/go-redsync/redsync/v4/redis/goredis/v9"
    )
    
    func main() {
    	// Create a pool with go-redis (or redigo) which is the pool redisync will
    	// use while communicating with Redis. This can also be any pool that
    	// implements the `redis.Pool` interface.
    	client := goredislib.NewClient(&goredislib.Options{
    		Addr: "localhost:6379",//redis 地址 端口
    	})
    	pool := goredis.NewPool(client) // or, pool := redigo.NewPool(...)
    
    	// Create an instance of redisync to be used to obtain a mutual exclusion
    	// lock.
    	rs := redsync.New(pool)
    
    	// Obtain a new mutex by using the same name for all instances wanting the
    	// same lock.
    	mutexname := "my-global-mutex"
    	mutex := rs.NewMutex(mutexname)
    
    	// Obtain a lock for our given mutex. After this is successful, no one else
    	// can obtain the same lock (the same mutex name) until we unlock it.
    	if err := mutex.Lock(); err != nil {
    		panic(err)
    	}
    
    	// Do your work that requires the lock.
    
    	// Release the lock so other processes or threads can obtain a lock.
    	if ok, err := mutex.Unlock(); !ok || err != nil {
    		panic("unlock failed")
    	}
    }
    

总结

总的来说,Redsync 是一个强大、健壮且易于使用的分布式锁解决方案,它能够轻松地融入你的 Go 应用程序,提升系统的并发控制能力。无论是新手还是经验丰富的开发者,都能从中受益。让我们一起探索 Redsync 的潜力,打造更可靠的分布式系统吧!

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值