redis的事务

1. 事务与管道的区别

事务提供了一种“将多个命令打包, 然后一次性、按顺序地执行”的机制, 并且事务在执行的期间不会主动中断 —— 服务器在执行完事务中的所有命令之后, 才会继续处理其他客户端的其他命令。

当你看到上面对事务的定义描述,难免会产生疑问,这个事务和管道有什么区别呢,看起来很像啊。

关于他们的区别,你可以重点掌握以下几点

  1. pipeline管道技术,是一种客户端行为,是客户端主动的缓存了命令然后在适当的时候将这些命令一次性的发送给redis服务器,而事务是服务端的行为。
  2. 执行事务时,redis服务器会阻塞其他客户端发过来的命令,但是执行管道中的命令则不会,假设一个事务中有100个命令,那么一定要等到这100个命令执行结束后,才会去执行其他客户端发过来的命令,如果管道中的命令有100个命令,执行到第50个时,有其他客户端的命令发过来,redis可能会去执行这个插队的命令,然后继续执行剩余的50个管道里的命令。
  3. pipeline技术是为了节约RTT,用空间换取时间,事务是为了解决操作的原子性。

2. 服务端事务的实现

客户端需要通过multi这个命令开启事务,服务端收到这个命令后,会对这个客户端的对象设置一个特殊的状态,进入这个状态后,客户端发送过来的后续命令不会立即执行,而是要等到客户端发送EXEC命令后,才会按照顺序执行已经缓存的所有命令。

有一点极容易搞错,千万不要以为你执行了multi命令后,其他客户端的命令就不会干扰和影响到你,redis的事务没有锁,它只是保证在收到exec命令,你之前发送的命令可以一次性执行完,仅是在这个过程中不会收到其他客户端命令的影响。在你发送exec命令之前,如果其他客户端的命令修改了某个key,而你在事务中也要操作这个key,那么它的命令执行在前,而你的命令执行在后,你的命令只有在服务端收到exec后才会真正被执行。

那么,怎么才能防止被别的客户端干扰呢,你可以使用watch

3. watch

WATCH 命令用于在事务开始之前监视任意数量的键: 当调用 EXEC 命令执行事务时, 如果任意一个被监视的键已经被其他客户端修改了, 那么整个事务不再执行, 直接返回失败, 如果你担心某个key被别人修改,你就watch这个key

from redis.client import Redis
from redis.client import WatchError

r = Redis(host='127.0.0.1', port=6379, db=0, password='198671724zds')
with r.pipeline()as p:
    try:
        p.watch('coolpython')
        value = int(p.get('coolpython'))

        p.multi()
        p.set('coolpython', value + 1)
        p.execute()
    except WatchError:
        print('coolpython 被修改')

print(r.get('coolpython'))

watch要配合事务来使用,如果不开启事务,这个watch是起不到监控作用的,执行exec时,如果被watch的key被其他客户端修改了,事务执行失败,上面的代码应当加上try ... execpt 来捕捉watch抛出的异常

4. 事务的 ACID 性质

redis的事务,保证了一致性和隔离性,但不保证原子性和持久性。

事务的原子性要求一组操作,要么都成功,要么都不成功,而redis在执行事务时,如果某个命令执行失败了,它并不会尝试进行重试或者回滚,因而不能确保原子性。

事务的持久性要求对系统的影响是永久的,但redis是基于内存的,在不启动持久化功能时,必然无法满足持久性,而即使是在RDB和AOF模式下,也无法保证持久性,在RDB模式下,有可能在RDB文件更新之前redis挂掉了,AOF模式下,事务的命令执行后,虽然会将数据写入到AOF文件里,但这个过程是后台异步进行的,仍然有失败的可能。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案