redisはどのように秒殺の超過販売問題を解決します
18615 ワード
1.reidsを用いたwatch+multi指令実現 watch+multi超販売問題を解決
1)原理ユーザが購入すると、WATCHによりユーザの在庫を傍受し、在庫がwatch傍受後に変化すると異常をキャプチャして在庫減額操作 を放棄する.在庫が変更を傍受することなく、数量が1より大きい場合、在庫数量は1減少し、タスク を実行する.
2)弊害 Redisトランザクションを完了しようとすると、トランザクションの失敗によって の再実行が繰り返される可能性があります.商品の在庫量が正確であることを保証することは重要であるが、WATCHを単純に使用するというメカニズムはサーバに圧力がかかりすぎる である.
2、reidsのwatch+multi+setnx指令を用いて実現
1)なぜ自分で鍵を作るのかには、同様のSETNXコマンドがRedis内のロックを実現する機能があるが、他のロックが提供するメカニズムは完全ではない である.でsetnxも分散ロックのいくつかの高度な特性を備えていないか、 を手動で構築する必要があります.
2)redisロックを作成する Redisでは、SETNXコマンドを使用するロックを構築することができる:rs.setnx(lock_name,uuid値) .でロックすることは、ランダムに生成する128ビットのUUIDにビットキーの値を設定し、他のプロセスによって を取得することを防止することである.
3)ロック解除ロックの削除操作は簡単で、対応するロックのkey値が取得したuuid結果を判断検証する のみである.適合条件(uuid値を判断)deleteによりredisから削除すればよい、pipe.delete(lockname) また、他のユーザが同名のロックを持っている場合、uuidの違いにより、検証後に他人のロック を誤って解放することはない.
4)ロックが解除できない問題の解決以前のロックでは、あるプロセスがロックを持つと突然プログラムがクラッシュし、ロックが を解放できないという問題も発生する.で他のプロセスがロックを持って作業を継続することができず、このような問題を解決するために、ロックを取得する際にロックを付加するタイムアウト機能 が可能となる. setnx+watch+multi超販売問題を解決 最適化:分散ロックにタイムアウト時間を加えるデッドロック防止
#! /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)原理
2)弊害
2、reidsのwatch+multi+setnx指令を用いて実現
1)なぜ自分で鍵を作るのか
2)redisロックを作成する
3)ロック解除
4)ロックが解除できない問題の解決
#! /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(.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)
# 3. ↑
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:
# , , , False
if rs.setnx(lock_name, identifier): #
# print(' : %s' % identifier)
rs.expire(lock_name, locked_time)
return identifier
time.sleep(.001)
return False