为什么需要分布式锁
一个服务是用分布式部署,那么当一个大量的请求来临,尤其时这些请求都会指向一个数据或者变量时,就会发生可怕的事情,谁也没法保证最后数据的正确性,更无法预判,那么,我们就应该来控制这些请求,让他们按照一定的顺序进行,或者说,按照自己预判的最终数据来设计策略,这时,就出现了,分布式锁,顾名思义,有锁,就有加锁,解锁等操作,目的就是为了同一时间,无论多少请求过来,我只让其中某一个请求来执行数据,这样就保证了数据在单位时间的唯一性。
Redis分布式锁
原理:
redis有一个设置key的方法,setnx(key, value),将 key 的值设为 value ,且仅当 key 不存在时,才能设置成功,若给定的 key 已经存在,则 SETNX 不做任何动作。
加锁:
加锁就是设置key value的过程
setnx(key, value)
释放锁:
释放锁,就是删除key的过程
delete(key)
用Python实现Redis分布式锁
# encoding: utf-8
import uuid
import time
import datetime
from threading import Thread
import redis
redis_client = redis.Redis(host="localhost",
port=6379,
db=10)
NUMBER = 10 # 票数
def acquire_lock(lock_name, acquire_time=10, time_out=5, identifier=None, thread_id=None):
"""获取一个分布式锁"""
now = datetime.datetime.now()
lock = "string:lock:" + lock_name
while (datetime.datetime.now() - now).seconds < acquire_time: # 获取锁的时候,防止异常获取时卡死导致其他线程无法获取此锁。
if redis_client.setnx(lock, identifier):
# 给锁设置超时时间, 防止进程崩溃导致其他线程无法获取锁
redis_client.expire(lock, time_out)
print("线程:{}--抢到锁了!".format(thread_id))
return identifier
else:
print("线程:{}--没有抢到锁!".format(thread_id))
break
return False
def release_lock(lock_name, identifier, thread_id):
"""通用的锁释放函数"""
lock = "string:lock:" + lock_name
value = redis_client.get(lock)
if value == identifier:
while redis_client.get(lock):
try:
print("线程:{}--释放掉了锁!".format(thread_id))
redis_client.delete(lock)
except redis.exceptions.WatchError:
print("线程:{}--释放锁异常!".format(thread_id))
pass
return True
print("线程:{}--这不是我的锁,我不能释放!".format(thread_id))
return False
def seckill(thread_id):
global NUMBER
lock_name = 'follow,me'
identifier = str(uuid.uuid4())
return_identifier = acquire_lock(lock_name, identifier=identifier, thread_id=thread_id)
if return_identifier:
print ("线程:{}--现在还有{}张票".format(thread_id, NUMBER))
if NUMBER < 1:
print("线程:{}--但是没抢到票,票抢完了".format(thread_id))
release_lock('follow,me', identifier, thread_id)
print("线程:{}--因为票抢完了,所以趁早释放锁".format(thread_id))
else:
NUMBER -= 1
print("线程:{}--抢到一张票,还剩{}张票".format(thread_id, NUMBER))
release_lock('follow,me', identifier, thread_id)
else:
print("线程:{}--抢锁失败".format(thread_id))
if __name__ == '__main__':
for i in range(50):
t = Thread(target=seckill, args=(i, ))
t.start()
time.sleep(0.01) # 根据具体的业务需求来设置,也可以不设置等待时间,这里设置等待时间是为了显示的更清楚。
代码已经print了注释,我就不多做解释了,若有疑问可以留言。
浅谈锁机制
因为有了锁,分布式,集群,多进程,多线程,协程等等都会因锁而降低性能,但是又不能不使用各种锁(分布式锁,进程锁(文件锁等等),线程锁),所以使用锁的时候,应该充分设计,根据业务需求,有独占、共享、排他等等锁类型,具体得看场景需要。