Redis学习笔记 ——(9)事务
事务是指一个完整的动作,要么全部执行,要么什么也没有做。
Redis 事务不是严格意义上的事务,只是用于帮助用户在一个步骤中执行多个命令。单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
Redis 事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
事务执行分为三个阶段:
- 开始事务
- 命令入队
- 执行事务
关键指令
Redis通过几个关键的失灵来实现事务
- MULTI
组装事务 - EXEC
执行事务 - DISCARD
取消事务 - WATCH
监听一些key,一旦有key在事务执行前发生变化,就取消事务的执行。 - UNWATCH
取消WATCH命令对所有key的监听
例子
//事务组装开始
127.0.0.1:6379> MULTI
OK
//命令入队
127.0.0.1:6379(TX)> SET cityName "beijing"
QUEUED
//命令入队
127.0.0.1:6379(TX)> GET cityName
QUEUED
//命令入队
127.0.0.1:6379(TX)> SET userName "gzy"
QUEUED
//命令入队
127.0.0.1:6379(TX)> GET userName
QUEUED
//命令入队
127.0.0.1:6379(TX)> SADD lis 1 2 3 4
QUEUED
//命令入队
127.0.0.1:6379(TX)> smembers lis
QUEUED
//命令执行,查看执行结果
127.0.0.1:6379(TX)> EXEC
1) OK
2) "beijing"
3) OK
4) "gzy"
5) (integer) 4
6) 1) "1"
2) "2"
3) "3"
4) "4"
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
对于事务的执行来说,如果 redis 开启了 AOF 持久化的话,那么一旦事务被成功执行,事务中的命令就会通过 write 命令一次性写到磁盘中去,如果在向磁盘中写的过程中恰好出现断电、硬件故障等问题,那么就可能出现只有部分命令进行了 AOF 持久化,这时 AOF 文件就会出现不完整的情况,这时,我们可以使用 redis-check-aof 工具来修复这一问题,这个工具会将 AOF 文件中不完整的信息移除,确保 AOF 文件完整可用。
事务错误
有关事务,大家经常会遇到的是两类错误:
- 调用 EXEC 之前的错误
调用 EXEC 之前的错误,有可能是由于语法有误导致的,也可能时由于内存不足导致的。只要出现某个命令无法成功写入缓冲队列的情况,redis 都会进行记录,在客户端调用 EXEC 时,redis 会拒绝执行这一事务。
可以看下面的例子:
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> ls
(error) ERR unknown command 'ls', with args beginning with:
127.0.0.1:6379(TX)> jhsl
(error) ERR unknown command 'jhsl', with args beginning with:
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
由于ls 和jhsl为redis不能是被的错误命令,因此exec执行的时候会直接报错。
2. 调用 EXEC 之后的错误
对于调用 EXEC 之后的错误,redis 则采取了完全不同的策略,即 redis 不会理睬这些错误,而是继续向下执行事务中的其他命令。这是因为,对于应用层面的错误,并不是 redis 自身需要考虑和处理的问题,所以一个事务中如果某一条命令执行失败,并不会影响接下来的其他命令的执行。
可以看下面的例子:
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name gzy
QUEUED
//name不是一个集合,下面这个命令会执行失败
127.0.0.1:6379(TX)> sadd name zy
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
WATCH 实现乐观锁
WATCH 本身的作用是监视 key 是否被改动过,而且支持同时监视多个 key,只要还没真正触发事务,WATCH 都会尽职尽责的监视,一旦发现某个 key 被修改了,在执行 EXEC 时就会返回 nil,表示事务无法触发。