Redis的事务实例分析

    Redis 中的使用

    Redis 通过 multi,exec,discard,watch 实现事务功能。

    • multi:开始事务

    • exec:提交事务并执行

    • Redis事务实例分析:从简单应用到复杂场景

      discard:取消事务

    • watch:事务开始之前监视任意数量的键

    >
    multi
    OK
    >
    set bookName "
    Redis"

    QUEUED
    >
    get bookName
    QUEUED
    >
    sadd tag "
    Redis"
    "
    New Book"

    QUEUED
    >
    smembers tag
    QUEUED
    >
    exec
    1) OK
    2) "
    Redis"

    3) (integer) 2
    4) 1) "
    Redis"

    2) "
    New Book"
    开始事务>
    multi
    OK

    这个命令将 Redis_multi 选项打开,让客户端从非事务状态变为事务状态

    命令入队>
    set bookName "
    Redis"

    QUEUED
    >
    get bookName
    QUEUED
    >
    sadd tag "
    Redis"
    "
    New Book"

    QUEUED
    >
    smembers tag
    QUEUED

    Redis命令不会立即执行,而是先进入一个先进先出的事务队列,直到事务被提交。QUEUED 表示这个命令已经入了事务队列。

    执行事务>
    exec
    1) OK
    2) "
    Redis"

    3) (integer) 2
    4) 1) "
    Redis"

    2) "
    New Book"

    当执行 exec 命令时,Redis 根据客户端所保存的事务队列, 以先进先出的方式执行事务队列中的命令:最先入队的命令最先执行, 而最后入队的命令最后执行。执行 exec 命令后,Redis 将结果存储在回复队列中并将该队列发送给客户端。客户端从事务状态退出,一个事务执行完毕。

    discard 命令>
    multi
    OK
    >
    set author "
    lisi"

    QUEUED
    >
    discard
    OK
    >
    get author
    (nil)

    discard 取消一个事务的命令,表示这个事务被取消。当客户端结束事务状态,回到非事务状态时,需要关闭 Redis_multi 选项。

    watch 命令# Redis 客户端1
    >
    watch letter
    OK
    >
    multi
    OK
    >
    set letter a
    QUEUED
    >
    exec
    (nil)


    # Redis 客户端2
    >
    set letter b
    OK

    # Redis 客户端1
    >
    get letter
    "
    b"

    在客户端1进入事务时,监控设置了letter键,并将其值设置为a,但该事务尚未提交。客户端2设置 letter 的 value 为 b。客户端1提交事务后返回的结果为 nil,但调用 get 命令可以得到 letter 的值为 b。这意味着当 letter 键被其他客户端改变后,事务将被取消,不会执行,并返回失败。

    watch 命令在事务开始之前监视任意数量的键:当调用 exce 命令执行事务时,如果任意一个被监视的键已经被其他客户端修改了,那么整个事务不再执行,直接返回失败。

    事务异常命令错误>
    set letter ac
    QUEUED
    >
    get letter ac
    (error) ERR wrong number of arguments for '
    get'
    command
    >
    exec
    (error) EXECABORT Transaction discarded because of previous errors.

    事务中命令异常属于语法错误,将导致事务无法执行。

    运行时异常>
    multi
    OK
    >
    lpush books "
    Redis"

    QUEUED
    >
    incr books
    QUEUED
    >
    lpush books "
    Python"

    QUEUED
    >
    lrange books 0 -1
    QUEUED
    >
    exec
    1) (integer) 1
    2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
    3) (integer) 2
    4) 1) "
    Python"

    2) "
    Redis"

    上面的例子是事务执行到中间遇到失败了,因为不能对一个字符串进行 incr 命令,事务在遇到命令执行失败后,后续的命令还继续执行,所以 books 的值能继续得到设置。这种异常只有程序员在代码中避免。

    事务的 ACID原子性

    原子意味着要么一起成功执行,要么一起失败回滚。Redis 提供的所有 API 都是原子操作。那么 Redis 事务只要保证在一批操作中保证原子性,但是在运行时异常中,在一个事务中一个命令出现异常,其他命令还是会继续执行,事务没有回滚机制,所以 Redis 事务是不保证原子性的。

    一致性

    事务异常

    如果命令错误事务无法执行,如果是运行时异常,Redis 会将错误包含在返回结果中,并不影响后续执行,所以事务是一致性的。

    Redis 进程被终结

    在纯内存模式下,Redis 没有做持久化,重启之后数据库是空白的,所以是事务一致性的。

    在 RDB 模式下,事务并不会在中途执行保存 RDB 文件的工作,只有在事务执行完后,RDB 工作才可能会开始。所以在事务执行过程中 Redis 进程被杀死,不管成功多少都不会保存到 RDB 文件中,所以是一致性的。

    在 AOF 模式下,事务部分语句被写入 AOF 文件并保存成功,不完整的事务被保存到了 AOF 文件,当重启 Redis 时,检查 AOF 文件不完整,Redis 退出并报错。需要把这段不完整的事务删除后才能重启成功,所以是一致性的。

    在 AOF 模式下,事务并未被写入 AOF 文件,所以重启后 Redis 数据库是最近一次成功保存到 AOF 文件中的数据。并没有这次事务的数据,所以是以一致性的。

    隔离性

    Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。所以事务是带有隔离性的。

    持久

    在纯内存模式下,事务肯定不是持续性的。

    在 RDB 模式下,服务器可能在事务执行之后、RDB 文件更新之前的这段时间失败,所以 RDB 模式下的事务也是不持久的。

    在 AOF 模式下,将命令添加到 AOF 文件中,但是对文件进行写入并不会马上写到磁盘上,而是先存储到缓冲区。所以数据保存到磁盘上有一段非常小的时间间隔。这种模式下事务也不是持久的。



    Redis是一个基于内存的键值存储数据库,其中的事务机制可以确保一组操作的原子性。在实际应用中,事务机制可以起到保证数据一致性和提高性能等多重作用。本文将以实例分析的方式,介绍Redis事务机制的基本原理及其在不同场景下的应用。
    一、基本场景下事务机制的应用
    在Redis中,事务机制通过MULTI, EXEC, DISCARD等指令完成。在基本使用场景下,我们可以使用Lua脚本来执行多个命令的调用和响应。例如,在对Redis进行批量插入数据时,我们可以使用MULTI封装多个SET命令,并通过EXEC或DISCRAD指令执行或取消这个批量操作。这种方式可以避免因为命令执行失败而导致的数据不一致的问题。
    二、分布式场景下事务机制的应用
    在分布式架构中,多个节点需协作完成任务,并发生数据应用和传输。事务机制可以确保这些操作的原子性,从而防止数据产生意外丢失和混乱。例如,在Redis Cluster中,事务机制的执行机制是增加执行命令的断言机制,即所有的节点都能成功执行命令并返回相同的结果,才算是成功执行这个事务。
    三、跨模块场景下事务机制的应用
    在面向对象的程序设计中,通常需要保证对不同实例的一组命令或接口的调用操作的原子性。Redis事务机制可以满足这一需求,并且与非Redis管道的数据传输接口(例如网络通信等)的效率相当。例如,将多个应用模块根据需求封装到同一个Redis事务中,在内存中完成并发规划,然后一次性向数据库提交,可以大幅度降低对数据库读写压力,提高整个系统的运作效率。
    综上,Redis事务机制是非常好用且重要的一个特性,在实际应用中应当充分发挥它的优点,同时注意它在各个场景下的不同要求和限制。