假设food_dict
是dictionary
来存储购物车中的商品,
{<food_id>:<count>}
(一个购物车可能包含多种食物)
我事先在food
存储了redis
:
r.hset('food:<food_id>', {'price': <price>, 'stock': <stock>})
在订购cart
时,我必须确保 stock
大于count
。
基本工具:
for k,v in food_dict.iteritems():
_stock = int(redis_db.hget('food:' + str(k), 'stock'))
if v > _stock:
# I have to rollback the decrement of stock
break
else:
redis_db.hset('food:' + str(k), 'stock', _stock - v)
要rollback
,需要pipeline
。
pipe = redis_db.pipeline()
for k, v in food_dict.iteritems():
_stock = int(redis_db.hget('food:' + str(k), 'stock'))
if v > _stock:
return
else:
pipe.hset('food:' + str(k), 'stock', _stock - v)
pipe.execute()
对于单个客户端,上面的代码可以很好地完成。谈到并发:
with redis_db.pipleline() as pipe:
while 1:
try:
pipe.watch(['food:' + str(k) for k in food_dict])
stock_dict = {}
for k in food_dict:
_stock = pipe.hget('food:' + str(k), 'stock')
stock_dict[k] = _stock
pipe.multi()
for k, v in food_dict.iteritems():
if v > stock_dict[k]:
break
else:
pipe.hset('food:' + str(k), 'stock', stock_dict[k] - v)
pipe.execute()
break
except WathchError:
continue
finally:
pipe.reset()
此代码是否可以保证订单只有在库存大于时才能?
答案 0 :(得分:1)
是的,如果更改了任何已观看的密钥,pipe.execute()
会出错。
然而。 WATCH/MULTI/EXEC
模式允许您实现乐观锁定,事务处理时间越长,它们就越有可能失败。此外,鉴于食品库存柜台很热,我不认为这种方法对您的使用情况有效。
相反,我会将事务实现为Lua脚本,首先检查每个项目的库存。然后我可以在那个剧本中决定是否提交购物车中的一些订单,所有订单,没有订单或其他任何订单。
修改强>
如何更有效地使用python进行热购物?
我猜你只能使用Python进行小的优化。核心区别在于,您现在正在管理&#34;交易&#34;在应用程序中,所以当有并发性时,它更有可能回滚。 Lua允许您在服务器上执行tx的逻辑,因此您基本上锁定了所有内容,但tx将成功(只要一旦启动就有足够的食物库存)。
对于次要优化,现在看起来food:*
是一个Hash密钥,它存储除stock
之外的其他内容 - 如果任何信息也发生变化,它将回滚正在运行的事务。您可以考虑为每个食品使用专用的库存密钥(例如food:apples:stock
)并改为观察。
由于您尝试提交整个购物车,也许您还应考虑将其分解为多个交易 - 购物车中的每个商品都有一个。这意味着如果您决定完全中止,您的应用程序将负责重试热门项目并回滚已提交的更改,这通常是购物车可以接受的(想想您在购物车中看到过多少次产品)或在杂货店的收银员附近)。
P.S。 Lua和Python非常相似,所以我真的鼓励你去接受它。它既有趣又简单,非常值得,因为您已经在使用Redis ......