01.redis分布式锁
https://www.cnblogs.com/xiaonq/p/12328934.html
1.1 redis事物介绍
-
1.redis事物是可以一次执行多个命令,本质是一组命令的集合。
-
2.一个事务中的所有命令都会序列化,按顺序串行化的执行而不会被其他命令插入
-
**作用:**一个队列中,一次性、顺序性、排他性的执行一系列命令
1 2 3 4 5 6
| # Multi 命令用于标记一个事务块的开始事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性( atomic )地执行 > multi incr books incr books > exec > discard
|
1.2 watch乐观锁
使用reids的 watch + multi 指令实现超卖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #! /usr/bin/env python # -*- coding: utf-8 -*- import redis def sale(rs): while True: with rs.pipeline() as p: try: p.watch('apple') # 监听key值为apple的数据数量改变 count = int(rs.get('apple')) print('拿取到了苹果的数量: %d' % count) p.multi() # 事务开始 if count> 0 : # 如果此时还有库存 p.set('apple', count - 1) p.execute() # 执行事务 p.unwatch() break # 当库存成功减一或没有库存时跳出执行循环 except Exception as e: # 当出现watch监听值出现修改时,WatchError异常抛出 print('[Error]: %s' % e) continue # 继续尝试执行
rs = redis.Redis(host='127.0.0.1', port=6379) # 连接redis rs.set('apple',1000) # 首先在redis中设置某商品apple 对应数量value值为1000 sale(rs)
|
1.3 setnx分布式锁
1 2 3
| > setnx lock:codehole true .... do something critical .... > del lock:codehole
|
1.3.1 setnx+watch+multi解决超卖问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| #! /usr/bin/env python # -*- coding: utf-8 -*- import redis import uuid import time
# 1.初始化连接函数 def get_conn(host,port=6379): rs = redis.Redis(host=host, port=port) return rs
# 2. 构建redis锁 def acquire_lock(rs, lock_name, expire_time=10): ''' rs: 连接对象 lock_name: 锁标识 acquire_time: 过期超时时间 return -> False 获锁失败 or True 获锁成功 ''' identifier = str(uuid.uuid4()) end = time.time() + expire_time while time.time() < end: # 当获取锁的行为超过有效时间,则退出循环,本次取锁失败,返回False if rs.setnx(lock_name, identifier): # 尝试取得锁 return identifier time.sleep(0.001) return False
# 3. 释放锁 def release_lock(rs, lockname, identifier): ''' rs: 连接对象 lockname: 锁标识 identifier: 锁的value值,用来校验 ''' pipe = rs.pipeline(True) try: pipe.watch(lockname) if rs.get(lockname).decode() == identifier: # 防止其他进程同名锁被误删 pipe.multi() # 开启事务 pipe.delete(lockname) pipe.execute() return True # 删除锁 pipe.unwatch() # 取消事务 except Exception as e: pass return False # 删除失败
'''在业务函数中使用上面的锁''' def sale(rs): start = time.time() # 程序启动时间 with rs.pipeline() as p: ''' 通过管道方式进行连接 多条命令执行结束,一次性获取结果 ''' while True: lock = acquire_lock(rs, 'lock') if not lock: # 持锁失败 continue try: count = int(rs.get('apple')) # 取量 p.set('apple', count-1) # 减量 p.execute() print('当前库存量: %s' % count) break finally: release_lock(rs, 'lock', lock) print('[time]: %.2f' % (time.time() - start))
rs = redis.Redis(host='127.0.0.1', port=6379) # 连接redis rs.set('apple',1000) # # 首先在redis中设置某商品apple 对应数量value值为1000 sale(rs)
|
1.3.2 优化:给分布式锁加超时时间防止死锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def acquire_expire_lock(rs, lock_name, expire_time=10, locked_time=10): ''' rs: 连接对象 lock_name: 锁标识 acquire_time: 过期超时时间 locked_time: 锁的有效时间 return -> False 获锁失败 or True 获锁成功 ''' identifier = str(uuid.uuid4()) end = time.time() + expire_time while time.time() < end: if rs.setnx(lock_name, identifier): rs.expire(lock_name, locked_time) return identifier time.sleep(.001) return False
|
__END__