Redis 教程:Redis 的事务详解

· Redis笔记 Redis 缓存 数据结构 入门笔记 NoSQL · 0 阅读 · 约 4 分钟

Redis 的事务是什么

Redis 的事务可以一次执行多个命令,并且保证这些命令按顺序串行执行,执行期间不会被其他客户端的命令插入。它和关系型数据库的事务不太一样:Redis 事务不保证原子性回滚,队列中某条命令执行出错时,其余命令仍会继续执行。

事务的三个阶段:

  1. 开始事务:MULTI
  2. 命令入队:后续命令不会立即执行,而是进入队列
  3. 执行事务:EXEC 依次执行队列中的命令

基本使用

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) OK
3) "v1"

入队阶段的命令都会返回 QUEUED,表示已加入事务队列;执行 EXEC 后按顺序返回每个命令的结果。

放弃事务

使用 DISCARD 可以清空事务队列并放弃执行:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> DISCARD
OK
127.0.0.1:6379> get k3
(nil)

事务中的两类错误

入队时出错(编译期错误)

比如命令拼写错误,这种错误会导致整个事务放弃执行:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> sett k1 v1
(error) ERR unknown command 'sett'
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

执行时出错(运行期错误)

比如对字符串执行 INCR,这类错误不会回滚,后续命令照常执行:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 abc
QUEUED
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
3) OK

可以看到 incr k1 失败了,但 set k2 v2 依然成功。所以 Redis 事务不保证原子性,需要自己在业务层处理这类错误。

WATCH 实现乐观锁

WATCH 可以监视一个或多个 key,如果在 EXEC 之前这些 key 被其他客户端修改,事务会被打断并返回 nil:

# 客户端 A
127.0.0.1:6379> set stock 100
OK
127.0.0.1:6379> WATCH stock
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> decr stock
QUEUED

在客户端 A 执行 EXEC 之前,如果客户端 B 执行了 set stock 50,那么客户端 A 的事务会失败:

127.0.0.1:6379(TX)> EXEC
(nil)

失败后需要 UNWATCH(或重新 WATCH)后重试,这就是典型的乐观锁实现,常用于秒杀、扣库存等场景。

小结

  • MULTI 开启事务,命令入队,EXEC 执行,DISCARD 放弃
  • 入队错误导致整个事务不执行,运行期错误不会回滚
  • Redis 事务不保证原子性,与 MySQL 事务有明显区别
  • WATCH 提供乐观锁能力,配合事务可以实现 check-and-set 逻辑