Redis - 事务
一、概述
和传统关系型数据库一样,Redis 同样是支持事务的。Redis 的事务可以通过 MULTI/EXEC/DISCARD/WATCH 等命令来实现。
二、事务的 ACID 特性
1). 原子性:事务中的命令要么全部执行,要么都不执行。
Redis 的事务和传统的关系型数据库事务最大的区别在于:Redis 事务不支持回滚,即使事务队列中某个命令执行期间出现了错误,整个事务也会继续执行下去,知道事务队列中所有命令执行完毕。
那么不支持回滚对 Redis 来说有何优势:不需要支持回滚,Redis 内部可以保证简单而快速。
不支持回滚是否合理:合理,事务队列中的命令在执行过程中,只会因为命令错误的语法而失败,而失败的命令是编程造成的,这些错误应该在开发过程中被发现,而不应该出现在生产环境中。
2). 一致性:数据库在事务执行之前是一致的,在事务执行后无论事务执行成功还是失败,数据库都应该是一致的。
“一致” 指的是数据符合数据库本身的定义和要求,没有非法或者无效的错误数据。Redis 通过谨慎的错误检测和简单的设计保证事务的一致性(详看本页:四)
3). 隔离性:各个事务之间不会相互影响,在并发状态下和在串行状态下执行事务的结果完全相同。
4). 持久性:当事务执行完毕后,结果保存到数据库中不会丢失。
- 无持久化机制下,事务部具有持久性:服务器停机重启后数据丢失。
- RDB 机制下,在特定条件下才会保存数据集快照,不能保证数据在第一时间被保存在硬盘中。
- AOF 机制下,appendfsync = always 时,程序总会在执行命令之后调用同步函数,将命令数据存在硬盘中,这种情况下的事务具有持久性。
- AOF 机制下,appendfsync = everysec 时候,程序会每秒同步一次数据到硬盘。因为停机可能就发生在命令执行完毕但是尚未同步的那一秒钟内,这样会造成事务数据丢失,故而不具有持久性。
- AOF 机制下,appendfsync = no,由操作系统决定何时将数据同步到硬盘。因为事务数据可能在等待同步的过程中丢失,这样会造成事务数据丢失,故而不具有持久性。
三、相关命令的用法
1). MULTI 与 EXEC:MULTI 用于开启事务,总是返回 ok。MULTI 执行后,后面的命令暂时不会执行,而是会存到队列中,等到 EXEC 执行之后,队列中的命令才会依次序执行。例子如下:
2). DISCARD 始终返回 ok,会清空事务队列,并且放弃执行事务。例子如下:
3). WATCH 命令(想看本页:五)
四、Redis 怎样处理事务中的错误
Redis 怎样处理事务中的错误来保证数据库的 “一致性” :
1). 入队错误
可能原因:命令不存在,命令格式不正确等
处理方式:Redis 会对入队失败的命令进行记录,当调用 exec 的时候,自动拒绝执行并放弃这个事务。
例子如下:INCR mykey 1 入队失败,因为命令格式不正确。
2). 执行错误
可能原因:命令语法错误(入队时无法检测,只有在执行的时候才会报错,比如事务中处理集合的命令用在了字符串上面)。
处理方式:事务中的命令执行失败,继续执行下一条命令,直至事务队列中的命令执行结束。
例子如下:LPOP mykey 报错,作用于 list 类型,不能用于字符串;但是不影响后续命令执行,+1 操作依然执行成功。
3). 服务器停机
- 如果服务器运行在无持久化的模式下,那么重启后的数据库是空白的,空白的数据库总是一致的。
- 如果服务器运行在 RDB 模式下,可以利用现有的 RDB 文件还原数据库到一个一致的状态。若 RDB 文件找不到,那么重启后的数据库是空白的,空白的数据库总是一致的。
- 如果服务器运行在 AOF 模式下,可以根据现有的 AOF 文件来恢复数据,将数据库还原到一个一致的状态。若 AOF 文件找不到,那么重启后的数据库是空白的,空白的数据库总是一致的。
综上所述,服务器停机不会影响数据库一致性。
五、WATCH 命令与乐观锁
WATCH 命令的返回值总是为 ok。
WATCH 命令本身就是一个乐观锁,它可以在 EXEC 命令执行之前,监视一定数量的 key,并在 EXEC 执行时,检查这些 key 是否被修改过,如果是的话,服务器就拒绝执行事务。例子如下:
时间 | 客户端A | 客户端B |
T1 | WATCH name | |
T2 | MULTI | |
T3 | SET name aa | |
T4 | SET name bb | |
T5 | EXEC |
客户端 A 执行事务的时候发现 name 的值被修改了,所以服务器拒绝执行这个事务。
如何取消对 key 的监视:
- WATCH 对 key 的监视从调用 WATCH 开始生效,直到调用 EXEC 为止。EXEC 被调用的时候不管事务是否执行,都会取消对 key 的监视。
- 另外当客户端断开连接后也会取消监视。
- 使用无参数的 UNWATCH 可以取消对所有 key 的监视。