BLOG
Enjoy when you can, and endure when you must.
OCT 18, 2013/数据库
学习笔记:Redis入门之事务

首先来看下面的例子:

... print conn.incr('notrans:')
... time.sleep(.1)
... conn.incr('notrans:', -1)
...
>>> if 1:
... for i in xrange(3):
... threading.Thread(target=notrans).start()
... time.sleep(.5)
...
1
2
3

这里启动了三个线程来模拟并发的情况,在没有事务的情况下,三个线程都可以在其他线程 Decrement 同时去执行 Increment。这不是我们的用意,并且在一些真实的情况下,这是会带来大麻烦的。下面我们在同样的操作上加上事务:

... pipeline = conn.pipeline()
... pipeline.incr('trans:')
... time.sleep(.1)
... pipeline.incr('trans:', -1)
... print pipeline.execute()[0]
...
>>> if 1:
... for i in xrange(3):
... threading.Thread(target=trans).start()
... time.sleep(.5)
...
1
1
1

可以看出,当采用事务提交时,每个线程都能保证在完成全部工作前不被其它的线程打扰。Redis-py 的 pipeline() 的奥秘在于,它在命令集的前后增加了 MULTI 和 EXEC。

MULTI 标记一个事务块的开始,随后的指令将在执行EXEC时作为一个原子执行。

Redis 里还包含另一组命令:WATCH 和 MULTI/EXEC 或 WATCH 和 UNWATCH/DISCARD

WATCH:标记所有指定的 key 被监视起来,在事务中有条件的执行(乐观锁)。当我们使用 WATCH 来监视 keys 时,如果在这期间有其它的 client 尝试对这些 keys 执行了修改等操作,那么在我们尝试调用 EXEC 时,Redis会执行失败并抛出错误提示。这样可以确保在执行重要的命令时所操作的数据不被他人修改。

UNWATCH/DISCARD 的用意则是取消监视,这两个命令不同之处在于:UNWATCH 仅刷新一个事务中已被监视的所有 key(也就是在 MULTI 之前);而 DISCARD 刷新一个事务中所有在排队等待的指令,并且将连接状态恢复到正常(如果已使用 WATCH,DISCARD 将释放所有被 WATCH 的 key),也就是在 EXEC 之前。

下面是一个购买商品的例子(摘录自《Redis in Action》):

    buyer = "users:%s"%buyerid
    seller = "users:%s"%sellerid
    item = "%s.%s"%(itemid, sellerid)
    inventory = "inventory:%s"%buyerid
    end = time.time() + 10
    pipe = conn.pipeline()
    while time.time() < end:
        try:
            pipe.watch("market:", buyer)
            price = pipe.zscore("market:", item)
            funds = int(pipe.hget(buyer, "funds"))
            if price != lprice or price > funds:
                pipe.unwatch()
                return None
            pipe.multi()
            pipe.hincrby(seller, "funds", int(price))
            pipe.hincrby(buyer, "funds", int(-price))
            pipe.sadd(inventory, itemid)
            pipe.zrem("market:", item)
            pipe.execute()
            return True
        except redis.exceptions.WatchError:
            pass
        return False

上述实现中,我们监视了 market,即市场,包含所有可以购买的商品,我们监视他以确保在完成交易之前,用户想要购买的商品没有被其他人抢走。同时还监视了购买者的信息,以避免在交易完成前用户就把腰包里的 money 花掉了。之后交易正式开始!下图展示了整个交易过程:

COMMENTS
LEAVE COMMNT