一、什么是分布式锁
分布式锁指的是在分布式场景中实现互斥类型的锁。
分布式是什么意思?分布式表示运行的节点可能在不同的机器或不同的网段中,节点间通信通过socket。互斥类型是什么意思?互斥类型表示同一时刻只允许一个执行体进入临界资源。
二、分布式锁的特性
分布式锁具有三大特性:
1、互斥性:同上所述,互斥性要求同一时刻只允许一个执行体进入临界资源。具体的操作包括加锁、解锁、给执行体打上唯一标记。
2、锁超时性:由于其分布式的特性(节点间需要通过网络进行通信),而一旦某获取了锁的节点出现网络故障或者进程宕机,从而无法释放锁,这将导致其他尝试获取该锁的节点或进程一直阻塞。因此,为避免该情况的发生,需要对其设置锁超时特性,即在某节点获取锁的时候设定一个时间,当超过该时间后该节点自动释放锁资源。
3、可用性:通俗来讲就是在合理时间内得到合理的回复。具体展开来又可细分为:
- 高可用性:确保即使部分节点故障,锁的机制仍然能保持有效,避免系统宕机。
- 网络分区容忍性:能够合理处理网络分区情况,确保系统的正常运行。
- 死锁防止:通过超时机制等方式,避免死锁的发生。
- 性能和响应时间:保持高效的锁操作,减少性能开销。
三、解决了什么问题
在分布式场景下,只允许一个节点执行某类任务。
四、分布式锁的实现问题
常见的分布式锁的实现方式为:
- 基于 Redis 实现分布式锁
- 基于 ZooKeeper 实现分布式锁
- 基于 Etcd 实现分布式锁
- 基于 Mysql 实现分布式锁
看到这读者是否有产生跟我一样的疑问:分布式锁在层次结构上应属于“基础组件”层次,而redis,mysql这些属于“中间件”层次,正常来说,中间件是在基础组件的基础上实现的,那为什么这里反而是基于中间件去实现基础组件呢?
原因在于,像redis这样的中间件,它们提供了构建分布式锁所需的原子操作,而这些原子操作是实现分布式锁的基础。换句话说,redis等这些中间件,它们的某些功能(SETNX EXPIRE)可以非常有效的用于实现分布式锁,我们基于redis实现分布式锁就是基于这些他们自带的功能。
这里以使用redis实现分布式锁举例:
import redis
import uuid
import time# 创建 Redis 连接
r = redis.StrictRedis(host='localhost', port=6379, db=0)lock_key = "my_lock"
lock_value = str(uuid.uuid4()) # 锁的唯一标识
lock_timeout = 10 # 锁的超时时间(秒)# 获取锁
def acquire_lock():# 使用 SETNX 设置锁并设置过期时间if r.setnx(lock_key, lock_value):r.expire(lock_key, lock_timeout)return Truereturn False# 释放锁
def release_lock():# 只有锁的持有者才能释放锁if r.get(lock_key) == lock_value:r.delete(lock_key)# 使用锁
if acquire_lock():try:print("Lock acquired, doing work...")time.sleep(5) # 模拟任务执行finally:release_lock()print("Lock released")
else:print("Unable to acquire lock")
SETNX
:用于设置一个值,仅当该键不存在时才设置。如果 Redis 返回True
,则表示锁成功获取;否则,表示锁已被其他客户端占用。expire
:为锁设置超时时间,避免死锁发生。如果客户端崩溃或网络中断,锁会在超时后自动释放。DEL
:释放锁时,首先需要检查当前锁值是否与客户端的值匹配,防止误删其他客户端的锁。
综上所述:
- Redis 本身并没有直接提供“分布式锁”功能,但它提供了支持实现分布式锁所需的基础操作(如
SETNX
、EXPIRE
)。 - 基于这些操作,我们可以构建一个分布式锁系统。因此,可以说 Redis 提供了实现分布式锁的基础构建块,但实现分布式锁的功能依赖于如何使用这些操作。
如果希望使用一个现成的分布式锁库,可以选择 Redisson,它是基于 Redis 的高层次封装,提供了直接的分布式锁功能。