- redis分布式锁要解决的问题:为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用并发处理相关的功能进行互斥控制。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的应用并不能提供分布式锁的能力。为了解决这个问题就需要一种跨机器的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!(可以保证在分布式部署的应用集群中,同一个方法在同一操作只能被一台机器上的一个线程执行,在传统的基于数据库的架构中,对于数据的抢占问题往往是通过数据库事务(ACID)来保证的。在分布式环境中,出于对性能以及一致性敏感度的要求,使得分布式锁成为了一种比较常见而高效的解决方案)
- 分布式锁应该具备哪些条件:
- 高可用的获取锁与释放锁
- 高性能的获取锁与释放锁
- 具备可重入特性
- 具备锁失效机制,防止死锁
- 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败
- 选用Redis实现分布式锁原因:
- Redis有很高的性能
- Redis命令对此支持较好,实现起来比较方便
- redis分布式锁常见命令
- setnx: SETNX key val:当且仅当key不存在时,创建一个kv的字符串锁,返回1;若key存在,则什么都不做,返回0
- expire:expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁
- delete:delete key:删除key
- 设计思想:
- 获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过value在释放锁的时候进行判断
- 获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁
- 释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放
- 代码(50个用户或线程抢10张票):
import time
import uuid
from threading import Thread
import redis
redis_client = redis.Redis(host="localhost",
port=6379,
db=0)
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
count = 10
def seckill(i):
identifier=acquire_lock('resource')
print("线程:{}--获得了锁".format(i))
time.sleep(1)
global count
if count < 1:
print("线程:{}--没抢到,票抢完了".format(i))
return
count-=1
print("线程:{}--抢到一张票,还剩{}张票".format(i,count))
release_lock('resource',identifier)
for i in range(50):
t = Thread(target=seckill,args=(i,))
t.start()