redis事务

238 阅读2分钟

背景

在多个线程对同一个变量修改的时候,往往会希望在自己修改的过程中,不要让其他线程来对此变量进行修改。我们在使用redis的时候,也是会产生这种类似的需求。

解决方案

Redis 的事务需要用到 MULTI 命令和 EXEC 命令,被MULTI和EXEC包围的命令相当于一个原子性的操作,执行的过程中不会夹杂着其他的redis操作。

不使用事务

在不使用事务,多个客户端对键no_trans:进行修改值的时候,会遇到一个背景中提到的问题。一个客户端修改的过程中,另外的客户端的命令也在对同一个键进行修改,就会造成结果不是咱想要的情况。下面的代码中通过多个线程来模拟这个过程,会得到变量no_trans:打印的结果是递增的,由1到3。

import time
import threading
from redis import Redis

redis_conn = Redis()

def no_trans():
    print redis_conn.incr("no_trans:")
    time.sleep(0.1)
    redis_conn.incr("no_trans:", -1)

def test(func):
    threads = []
    for i in range(3):
        thread = threading.Thread(target=func)
        thread.start()
        threads.append(thread)
    # 等待每个线程执行完毕
    for thread in threads:
        thread.join()
    
if __name__ == '__main__':
    test(no_trans)

使用事务

在使用了事务的情况下就不会这样了。python中使用过pipeline来标识事务,函数默认参数是True,在执行了pipeline.execute()的时候,多个命令会一起传递给redis服务器,中间不会掺杂其他命令的的顺序执行。为了避免redis和客户端之间的网络消耗,也可以给pipeline函数传递参数False,多个命令同时传递给redis,不过执行的过程中不会按照事务来进行处理,命令执行的过程中可能会执行其他命令。由于每个事务执行的过程中是不会有其他的命令打扰的,所以在使用事务对上面的操作修改之后,就不会出现一样的问题了。

def trans():
    pipeline = redis_conn.pipeline()
    pipeline.incr("trans:")
    time.sleep(0.1)
    pipeline.incr("trans:", -1)
    print pipeline.execute()[0]
    
if __name__ == '__main__':
    test(trans)