Redis:Redisson看门狗续锁实现分布式锁的原理,及如何避坑
在微服务环境中,分布式锁用于保护共享资源的并发安全,保证多个进程、线程有序访问资源。Redis实现分布式锁的关键要素如下: 1、互斥性保证资源的串行访问,防止并发冲突。
2、设置锁过期时间,避免服务异常导致的锁挂问题。
3、自动续锁超时机制,防止业务超时,保证互斥。
4、Lua脚本实现多条指令的原子性,保证锁操作的完整性。
5、可重入性利用线程ID信息来区分来自同一线程的请求,避免误删除锁。
6、防止误删除锁的策略,比如通过全局唯一ID判断是否是自己的锁。
7、锁等待机制通过发布订阅模型通知等待锁的线程。
Redisson看门狗锁更新实现分布式锁的原理是基于RedissonLock的tryLock方法。
当锁超时设置为-1并且获取锁成功时,会启动一个定时任务自动更新锁,直到锁被释放。
加锁和解锁操作通过Lua脚本实现,保证原子性。
解锁过程通过发布订阅机制通知等待锁的线程。
需要注意的是,单实例Redis的锁实现存在主从切换和数据丢失的问题。
推荐使用Redisson提供的红锁算法来解决集群锁问题。
在实现分布式锁时,需要避免传递自定义的锁超时,保证加锁和释放操作在同一个线程中执行,隔离业务和监控Redis实例的状态,并使用幂等设计来处理可能出现的异常。
利用Redis对批量数据实现分布式锁
在开发收入报表平台时,需要在界面上定义一个聚合按钮,以实现将多个数据聚合到报表中的操作。为了防止多个用户同时操作同一份数据,我们自主设计并实现了基于Redis的分布式锁定解决方案。
在逻辑代码中,我们使用了特定的Redis锁定方法。
首先,您必须提供传输数据的主键 ID 集 (scIds),以及最大锁定超时 (timeOutToDeleteRedisKey) 和租户 ID (organizationId) 作为参数。
该方法通过批量获取每个数据项的Redis锁来实现锁操作。
一旦锁定失败,锁定的锁定将被取消,并提示用户“有程序正在运行,正在采集数据,请刷新页面稍后再试”。
如果业务代码执行成功或者出现错误,系统会自动解锁当前锁定的数据。
实现效果如下:在执行批量加锁操作时,如果加锁过程失败,系统会释放之前添加的锁,并向用户发送错误信息。
如果业务逻辑执行成功或失败,系统将自动解锁被利用的数据。
为了保证代码的完整性和可用性,我们还提供了一系列相关的学习资料和面试题、架构和设计资料供感兴趣的开发者下载。
如果您需要获取此信息,请在私信中回复【07】即可免费获取。
redis分布式锁的实现(setNx命令和Lua脚本)
分布式锁用于多线程环境中,保证同一时刻只有一个线程可以访问共享资源,特别是在分布式系统中,这一要求更为重要。在Java中,本地锁是通过synchronized关键字和reentrantLock来实现的,但是,在分布式架构中,必须使用分布式锁机制来保证不同节点上的线程能够同步,防止数据冲突并避免重复操作。
系统连续性。
分布式锁的主要特性包括:互斥性、原子性、一致性、可撤销性。
其主要实现方式包括使用Redis等分布式缓存系统。
本文主要讨论基于Redis的分布式锁的实现,重点介绍setnx+expire命令和Lua脚本的使用。
它还提到了更先进的 redlock 算法和工具(如 Radisson)的实现。
###1。
使用setnx+expire命令强制分布式锁(错误的做法) - **setnx**:用于设置键值,仅当键不存在时才设置,并且是原子性的。
**过期**:设置key的过期时间,实现超时机制。
-**错误**:`setnx`和`expire`分开执行,不保证原子性。
如果应用程序遇到异常或在“setnx”成功后重新启动,锁可能无法释放。
##2. 使用Lua脚本实现分布式锁 - **改进方案**:Lua脚本可以一次执行多个Redis命令,保证原子性。
具体实现为: lualocalkey=KEYS[1]localvalue=ARGV[1]localtimeout=tonumber(ARGV[2])redis.call('setnx',key,value)redis.call('expire',key,timeout) ###3。
使用 `setkeyvalue[exseconds][pxmilliseconds][nx|xx]` 命令 - **优点**:从 Redis 版本 2.6.12 开始,`SET` 命令在 `setnx` 命令中添加了 `nx` 选项达到同样的效果。
-**唯一性**:设置唯一值,通常使用UUID,保证唯一性,避免共享资源冲突。
-**释放锁**:需要验证锁的唯一值并通过Lua脚本实现原子性:lualocalkey=KEYS[1]localvalue=ARGV[1]localexpected=ARGV[2] redis.call(' get ' ,key,function(err,stored_value)iferrtthenreturn nnilendifstored_value==expectedthenredis.call('del',key)elsereturnnilenend)###4.redlock 算法和Radisson实现 - **Redislock**:Redis作者Antirez提出了基于多个独立节点的分布式锁,以提高可用性和可靠性。
-**Radisson**:提供简单易用的分布式锁实现,通过Radislock算法进行优化,支持Java和Netty非阻塞I/O,兼容JUC的锁接口。
###5。
Redis实现分布式锁的循环——**SpringBoot+Jdis+AOP**:创建一个简单的分布式锁实现,包括自定义注解、AOP拦截器和核心类实现,最后通过controller层控制测试。
通过上述方法,我们可以实现基于Redis的分布式锁,保证分布式系统的数据一致性和线程安全。
分布式锁的实现不仅涉及到技术细节,还涉及到各种场景还需要考虑优化和扩展,比如处理Redis集群环境中的锁冲突等问题,保证系统的稳定性和效率。
python使用四种方案(Redis,Zookeeper,etcd,mysql)实现分布式锁代码实例
分布式密钥在分布式系统中发挥着重要作用,用于解决多个节点之间共享资源的访问问题。下面介绍四种实现方法及其代码示例,分别使用Redis、ZooKeeper、etcd和MySQL。
1、Python结合Redis实现分布式密钥 Redis支持原子操作,非常适合实现分布式密钥。
下面是使用Redis实现分布式锁的代码示例: pythonimportredisdefdistributed_lock(key):r=redis.Redis(host='localhost',port=6379,db=0)lock_key='lock:'+key#生成一个字符串随机,作为唯一标识 lock_id=str(uuid.uuid4())#设置过期时间为5分钟锁,如果过期还没有释放,则释放 自动 ifnotr.set(key_key,key_id,ex=300,nx=True): #如果设置失败,则表示key被其他进程获取,返回FalsereturnFalse #key获取成功,返回TruereturnTrue 2.Python结合ZooKeeper实现分布式密钥 ZooKeeper提供了高度一致的分布式协调服务,利用其特性可以实现分布式密钥。
下面是使用 ZooKeeper 实现分布式锁的代码示例: pythonfromkazoo.clientimportKazooClientdefdistributed_lock(key):zk=KazooClient(hosts='localhost:2181')zk.start()lock_path='/lock/'+key#创建一个锁定节点,如果已经存在则等待该节点前面的子节点完成操作并获取锁 zk.ensure_path(lock_path)try:#获取锁 zk.create(lock_path,b'data',ephemeral=True) exceptKazooExceptionase:#Lock已被其他进程获取,返回FalsereturnFalse#Lock成功获取,返回TruereturnTrue 3.Python结合etcd实现分布式 Etcd是分布式键值适合实现分布式密钥的存储系统。
下面是使用etcd实现分布式锁的代码示例: pythonfrometcd3importEtcd3Clientdefdistributed_lock(key):client=Etcd3Client(host='localhost',port=2379)lock_path='/lock/'+key#创建锁节点,如果是已经存在,则等待该节点前面的子节点完成操作并获取锁 client.put(lock_path ,'data',dir=True)try:#获取客户端 key.put(lock_path+'/data','data',dir=True) exceptEtcdKeyAlreadyExistase:#密钥已被其他进程获取,返回FalsereturnFalse#密钥获取成功,返回TruereturnTrue 4.使用MySQL实现分布式密钥中低并发场景下,可以使用MySQL数据库作为替代方案来实现分布式密钥。
下面是使用MySQL实现分布式锁的示例代码: pythonimportpymysqldefdistributed_lock(key):conn=pymysql.connect(host='localhost',port=3306,user ='root',password='password',db=' db_name',charset='utf8mb4')cursor=conn.cursor()lock_key='lock:'+keysql=f"SELECT*FROMlo ck_tableWHEREkey='{lock_key}'FORUPDATE"#Lock SQLtry:cursor.execute(sql)conn.commit() exceptpymysql.err.OperationalError:#Lock已被其他进程获取,返回FalsereturnFalse#Key成功获取,返回TruereturnTrue示例上面的代码展示了结合Redis、ZooKeeper、etcd和MySQL来实现分布式密钥需求的基本Python方法。
注意,本示例代码需要根据实际环境和具体需求进行调整。
高并发必备,使用Redis分布式锁必须注意的10个细节
在分布式系统中,实现分布式锁定是一个常见的需求。
为了提高性能,Redis被用作分布式锁定工具。
然而,实现高性能和数据安全的分布式锁定并不容易。
下面我们将讨论分布式锁应具备的特点,以及使用Redis实现分布式锁时的一般技巧和注意事项。
分布式锁必须满足以下特性:
互斥性:保证同一时刻只有一个线程可以获得锁。原子性:加锁和解锁过程应该被视为不可分割的原子操作。
耐用性:即使在异常情况下,锁也必须保持功能。
可撤销性:即使您的业务代码失败,也允许您释放锁。
可重入:允许同一个线程多次获取同一个锁。
使用Redis实现分布式锁有以下10种方式,每种方式都有自己的优缺点。
下面我们重点分析几个常见的技巧:
使用setnx Redis命令可以实现分布式锁。
该命令的作用是设置键值对。
示例实现如下:
但是这种方法有一个缺点:如果执行业务代码时出现异常,锁可能永远不会被释放。
要解决这个问题,应该保证业务代码在try/finally块中执行,以保证无论是否出现异常都能释放锁。
单纯设置过期时间并不能防止服务器宕机时锁释放失败。
为了解决这个问题,应该将锁定和过期操作合并为原子操作。
从 Redis 2.6.12 版本开始,使用setnx命令一次性设置过期时间是一个原子操作。
示例命令:`SETkeyvalue[EXseconds][PXmilliseconds][NX|XX]`。
解锁锁时,应避免诸如可能释放其他线程上的锁的非原子操作之类的问题。
正确的做法是在释放锁时判断lock_value是否属于当前线程,并使用Lua脚本来实现原子操作。
为了防止锁过期时其他线程被抢占,需要实现自动更新锁的功能。
通过检测周期并自动延长锁的使用寿命,确保锁的使用寿命。
在事务中加锁和释放锁时,必须保证在发送事务前不释放锁,以避免互斥锁失败。
正确的做法是将锁定和解锁操作从事务逻辑中分离出来,分别在事务开始和结束时执行。
要实现可重入锁定,需要增加一个计数器来记录当前线程锁定的次数。
每加锁一次,计数器就加一; 当锁被移除时,计数器减一; 当计数器达到零时,释放锁。
使用Redis集群锁定时,互斥锁可能会因故障转移而失效。
Redlock(Distributed Redis Locking)通过计算多个Redis实例的锁定状态来保证锁定的可靠性,提供了集群锁定问题的解决方案。