任务:
- 使用事务来解决一个竞争问题
现在介绍一下事务:Redis将一些列命令序列化,然后按照顺序执行,在一个事务执行的过程中,停止执行其他Redis客户端的请求,使得一个事务体现出和一个命令一样的原子性。
下面这段代码是没有用到事务的:
ONE_WEEK_IN_SECONDS = 7 * 86400
VOTE_SCORE = 432
def article_vote(conn, user, artivle):
cutoff = time.time() - ONE_WEEK_SECONDS
if conn.zsocre('time:', article) < cutoff:
return
article_id = article.partition(':')[-1]
if conn.sadd('voted:' + article_id, user):
conn.zincrby('sorce:', article, VOTE_SCORE)
conn.hincrby(article, 'votes', 1)
复制代码
下面这一段是使用事务:
ONE_WEEK_IN_SECONDS = 7 * 86400
VOTE_SCORE = 432
def new_article_vote(conn, user, article):
cutoff = time.time() - ONE_WEEK_SECONDS
if conn.zsocre('time:', article) < cutoff:
return
article_id = article.partition(':')[-1]
pipeline = conn.pipline()
pipeline.sadd('voted:' + article_id, user);
pipeline.zincrby('sorce:', article, VOTE_SCORE)
pipeline.hincrby(article, 'votes', 1)
pipeline.execute()
复制代码
- 游戏玩家出售背包中的道具,将道具放到商场上
接下来实现一个能够记录游戏用户信息记录和用户背包内道具的例子使用到的知识点有事务,wacth,unwacth等:
def list_item(conn, itemid, sellerid, price):
inventory = "inventory:%s" % sellerid
item = "%s:%s" % (itemid, sellerid)
end = time.time() + 5;
pipe = conn.pipeline() // 流水线
while time.time() < end:
try:
pipe.watch(inventory) // 监视用户背包,防止在事务执行前被修改
if not pipe.sismember(inventory. itemid): // 检查用户是否有将要被出售的道具
pipe.unwatch() // 取消监视
return None
pipe.multi() // 开启一个事务
pipe.zadd('market:', item, price) // 将商品放入有序集合中(商场)
pipe.srem(inventory, itemid) // 移除用户背包中的要出售道具
pipe.execute() // // 事务执行,流水线发送给redis服务器
return True
expect redis.exceptions.WatchError: // 用户背包发生了变化,就回抛出异常
pass
复制代码
- 游戏用户购买商品
def purchase_item(conn, buyerid, itemid, sellerid, lprice):
buyer = "users:%s" % buyerid
seller = "users:%s" % sellerid
item = "%s:%s"%(itemid, sellerid)
inventory = "inventory:%s" % buterid
end = tiem.time()
pipe = conn.pipeline()
while time.time() < end
try:
pipe.watch("makert:", buyer); // 对商品买卖市场和买家进行监视
price = int(pipe.zscore('market:', item) // 获取商场有序集合中item的分数,也就是价格
funds = int(pipe.hget(buyers, "funds")) // 获取游戏玩家拥有的货币数
if funds != lprice or price > funds:
pipe.unwatch()
return None
pipe.multi()
pipe.hincrby(seller, 'funds', price) // 给卖家加卖掉道具的钱
pipe.hincrby(buyer, 'funds', -price) // 买家扣除买道具的钱
pipe.sadd(inventory, itemid) // 将买来的道具加入买家背包(注意只是存入道具id)
pipe.zrem('market:', item) // 移除商场上的道具记录
pipe.execute() // 事务执行,流水线发送给redis服务器
return True
except redis.exceptions.WatchError: //如果买家的个人信息和商场在事务执行中发生变化,就抛出和这个异常
pass
return fasle
复制代码
最后将明确几个概念,事务和非事务流水线
事务可以将一系列命令保存其中,一个事务执行过程中不会受到其他客户端的影响。 非事务流水线是将一系列命里,一次性发给Redis服务器,能够减少客户端和Redis服务器的交互,缺点会受到其他客户端的影响,但是效率比事务高。