分布式锁介绍
分布式锁: 在分布式的环境中加锁,通俗点就是在集群环境下加锁
用途: 用在抢购,预售等用途,用来保证多台机器上的数据并发安全
mysql中的悲观锁,乐观锁就是分布式锁的一种,但是mysql的性能很低,在高并发的环境下,一般不用mysql的锁,而使用其他的分布式锁。
使用可以基于redis实现,并且非常简单,官方提供了Redlock模块调用,
from redlock import RedLock
lock = RedLock('distribted_lock')
lock.acquire() # 获取锁
# 抢到锁之后的逻辑
do_something()
lock.release() # 释放锁
内部逻辑
-
使用命令介绍
(1)SETNX
SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。
(2)expire
expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。
(3)delete
delete key:删除key
在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。 -
实现思想
(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
demo
# 连接redis
redis_client = redis.Redis(host="localhost",
port=6379,
password=password,
db=10)
# 获取一个锁
#lock_name:锁定名称
# acquire_time: 客户端等待获取锁的时间
# time_out: 锁的超时时间
def acquire_lock(lock_name, acquire_time=10, time_out=10):
"""获取一个分布式锁"""
identifier = str(uuid.uuid4())
end = time.time() + acquire_time
lock = "string:lock:" + lock_name
while time.time() < end:
if redis_client.setnx(lock, identifier):
# 给锁设置超时时间, 防止进程崩溃导致其他进程无法获取锁
redis_client.expire(lock, time_out)
return identifier
elif not redis_client.ttl(lock):
redis_client.expire(lock, time_out)
time.sleep(0.001)
return False
#释放一个锁
def release_lock(lock_name, identifier):
"""通用的锁释放函数"""
lock = "string:lock:" + lock_name
pip = redis_client.pipeline(True)
while True:
try:
pip.watch(lock)
lock_value = redis_client.get(lock)
if not lock_value:
return True
if lock_value.decode() == identifier:
pip.multi()
pip.delete(lock)
pip.execute()
return True
pip.unwatch()
break
except redis.excetions.WacthcError:
pass
return False
补充
分布式id的选择
- 选择mysql的自增类型,性能不高
- 使用当前时间戳,多台机器,同一个时刻,id会重复
- 使用uuid,数据量大,也可能会重复
- 多台服务器生成不重复id: 推荐使用雪花算法,并且python有现成的模块可以实现
分布式锁还可以通过ZooKeeper实现