基于Redis实现分布式锁

基本概念:

分布式锁,是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。


举个例子:

1.假设有一个进程A,每小时准点给用户发送一条短信"Hello world",为了高可用,就必须在多台机器上面部署多个进程,避免宕机的情况

2.假设部署在两台机器,那么问题来了,用户每个小时就会收到两条"Hello world",信息就重复了

3.我们希望只发送一条"Hello world",那么就可以引入分布式锁的概念了,

4.进程A和进程B发送短信前先去注册一个锁,假设进程A抢到了锁,进程B就等待结果,如果发送成功了,那么就B就放弃此次任务,等待下一个小时。

5.问题的核心就在于怎么注册锁,检查锁的存在和注册锁是一个原子性操作,类似MySQL的主键,存在则不能insert,就说是你不能把我的锁覆盖了,你得等着

6.我们有多种方式可以实现分布式锁,最简单的就是以每小时准点这个时间作为主键,到mysql写入一条数据,利用数据库来维持一致性

7.当然分布式锁也会存在很多暗坑,不在这里展开。


分布式锁的几种实现:

1.zookeeper分布式锁,基于自增节点

2.redis分布式锁,基于setnx命令,

基于Redis实现分布式锁:http://blog.csdn.net/daiyudong2020/article/details/51760648

官网:http://redis.io/topics/distlock

译文:http://www.oschina.net/translate/redis-distlock

3.memcache分布式锁,基于add函数

这几种方案在网上有很多技术文章,不重复叙述,需要的Google一下


分布式锁的基本功能:

1.同一时刻只能存在一个锁
2.需要解决意外死锁问题,也就是锁能超时自动释放
3.支持主动释放锁

分布式锁解决什么问题:

多进程并发执行任务时,需要保证任务的有序性或者唯一性

准备:
redis版本>=2.6
redis是主从+sentinel模式(为了高可用)

原理:
redis2.6之后,SET命令支持超时和key存在检查,这是一个原子操作

获取锁并设置超时时间:
SET key value [EX seconds] [PX milliseconds] [NX|XX]

删除锁:
DEL key

EX:单位是秒
PX:单位是毫秒
NX:如果key存在,返回nil(失败),不存在返回ok
XX:如果key存在,返回ok,不存在返回nil(失败)

基本操作如图示:


案例:
有一个系统,需要对用户信息进行增删改查操作,系统是多进程的,要求对用户的操作是有序的串行的


上测试代码,可以多开进程观看效果:

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. # -*- coding: utf-8 -*-  
  2.   
  3. import time  
  4. import redis  
  5.   
  6. # 获取锁  
  7. def get_lock(uid):  
  8.     # 连接redis  
  9.     r = redis.StrictRedis(host='localhost', port=6379, db=0, socket_timeout=1, socket_connect_timeout=3)  
  10.       
  11.     # 当前时间戳,用于删除锁时check  
  12.     sec = str(time.time())  
  13.       
  14.     # 处理超时时间  
  15.     timeout = 300  
  16.       
  17.     # 试图锁住uid  
  18.     while True:  
  19.         res = r.set(uid, sec, ex=timeout, nx=True)  
  20.         if res == True:  
  21.             print "get lock succeed, return"  
  22.             return True, sec  
  23.         else:  
  24.             print "get lock failed, lock exist, wait"  
  25.             time.sleep(0.001)  
  26.     return FalseNone  
  27.   
  28.   
  29. # 释放锁  
  30. def del_lock(uid, sec):  
  31.     # 连接redis  
  32.     r = redis.StrictRedis(host='localhost', port=6379, db=0, socket_timeout=1, socket_connect_timeout=3)  
  33.   
  34.     # 校验  
  35.     redis_sec = r.get(uid)  
  36.     if sec != redis_sec:  
  37.         print "check permission failed :%s" % uid  
  38.         return False  
  39.     print "check permission succeed :%s" % uid  
  40.   
  41.     #删除  
  42.     res = r.delete(uid)  
  43.     if res:  
  44.         print "del key succeed :%s" % uid  
  45.         return False  
  46.     else:  
  47.         print "del key failed :%s" % uid  
  48.         return True  
  49.   
  50.   
  51. if __name__ == '__main__':  
  52.   
  53.     uid = "001"  
  54.   
  55.     while True:  
  56.         status, sec = get_lock(uid)  
  57.         if status:  
  58.             del_lock(uid, sec)  
  59.         time.sleep(0.001)  



缺陷:

a) 一旦redis发生主从切换,可能会丢失一些锁,

b) 如果对锁的要求很高,可以参考redis官方提供的方案:http://redis.io/topics/distlock

c) 范例代码只能当作原理来理解,实际上有很多需要优化的地方,优化可参考:https://pypi.python.org/pypi/python-redis-lock


End;


原文:http://blog.csdn.net/daiyudong2020/article/details/51760648

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于 Redis 实现分布式锁可以利用 Redis 的原子性操作和超时特性来实现。下面是一个基本的实现思路: 1. 获取锁:使用 Redis 的 SETNX 命令,如果指定的锁 key 不存在,则设置该 key 的值为当前时间戳加上锁的超时时间,并返回成功;否则,返回失败。 2. 释放锁:使用 Redis 的 EVAL 命令,通过 Lua 脚本来实现原子性的删除锁。脚本的内容是先判断锁是否存在且超时,如果是则删除锁并返回成功;否则,返回失败。 下面是一个简单的 Python 代码示例: ```python import redis import time class RedisLock: def __init__(self, redis_client, lock_key, expire_time): self.redis = redis_client self.lock_key = lock_key self.expire_time = expire_time def acquire(self): while True: timestamp = int(time.time() * 1000) + self.expire_time acquired = self.redis.set(self.lock_key, timestamp, nx=True, px=self.expire_time) if acquired: return True time.sleep(0.001) def release(self): lua_script = """ if redis.call("exists", KEYS[1]) == 1 then local current_value = tonumber(redis.call("get", KEYS[1])) if current_value and current_value <= tonumber(ARGV[1]) then return redis.call("del", KEYS[1]) end end return 0 """ self.redis.eval(lua_script, 1, self.lock_key, int(time.time() * 1000) + self.expire_time) # 使用示例 redis_client = redis.Redis(host='localhost', port=6379, db=0) lock = RedisLock(redis_client, 'my_lock', 1000) # 锁的超时时间为 1000 毫秒 if lock.acquire(): try: # 执行需要加锁的代码 pass finally: lock.release() ``` 需要注意的是,以上代码仅是一个简单的实现示例,实际使用中还需要考虑异常处理、锁的可重入性、锁的可拥有时间等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值