什么是Redis的事务
Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
Redis事务的三个特性:一致性,顺序性,排他性
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
Redis事务不可回滚
相关指令解读
序号 | 命令及描述 |
---|---|
1 | DISCARD 取消事务,放弃执行事务块内的所有命令。 |
2 | EXEC 执行所有事务块内的命令。 |
3 | MULTI 标记一个事务块的开始。 |
4 | UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
5 | WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
MULTI
Redis Multi 命令用于标记一个事务块的开始。事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。
代码语言:javascript复制redis 127.0.0.1:6379> MULTI # 标记事务开始
OK
redis 127.0.0.1:6379> INCR user_id # 多条命令按顺序入队
QUEUED
redis 127.0.0.1:6379> INCR user_id
QUEUED
redis 127.0.0.1:6379> INCR user_id
QUEUED
redis 127.0.0.1:6379> PING
QUEUED
redis 127.0.0.1:6379> EXEC # 执行
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
返回值:总是返回 OK 。
EXEC
Redis Exec 命令用于执行所有事务块内的命令。
代码语言:javascript复制# 事务被成功执行
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> INCR user_id
QUEUED
redis 127.0.0.1:6379> INCR user_id
QUEUED
redis 127.0.0.1:6379> INCR user_id
QUEUED
redis 127.0.0.1:6379> PING
QUEUED
redis 127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
返回值:事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。
DISCARD
Redis Discard 命令用于取消事务,放弃执行事务块内的所有命令。
代码语言:javascript复制redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> PING
QUEUED
redis 127.0.0.1:6379> SET greeting "hello"
QUEUED
redis 127.0.0.1:6379> DISCARD
OK
返回值:总是返回 OK 。
WATCH
Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断(也就是在这个事务里面的全部指令都会失效)
代码语言:javascript复制redis> WATCH lock lock_times
OK
UNWATCH
Redis Unwatch 命令用于取消 WATCH 命如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。令对所有 key 的监视, 因为 EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD 命令在取消事务的同时也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行 UNWATCH 了。
代码语言:javascript复制redis 127.0.0.1:6379> WATCH lock lock_times
OK
redis 127.0.0.1:6379> UNWATCH
OK
返回值:总是返回 OK 。
执行事务过程特殊情况
编译型异常
代码语言:javascript复制特点:事务中有错误的命令,会导致默认放弃事务,所有的命令都不会执行
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set name ls
QUEUED
127.0.0.1:6379> append name2 # 错误的命令
ERR wrong number of arguments for 'append' command
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> EXEC # 执行事务,出现编译型异常
EXECABORT Transaction discarded because of previous errors.
运行时异常
代码语言:javascript复制特点:在事务执行的过程中语法没有出现任何问题,但是它对不同类型的key执行了错误的操作,Redis只会将返回的报错信息包含在执行事务的结果中,并不会影响Redis事务的一致性
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name ls
QUEUED
127.0.0.1:6379> incr name # 语法正确,但是对一个String类型的k1执行了错误的操作
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec # 执行事务,出现运行时异常
OK # 执行ok
ERR value is not an integer or out of range # 命令报错,但是不影响事务整体的运行
ls# 依旧执行
为什么 Redis 不支持回滚
如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。
以下是这种做法的优点:
- Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
- 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。
举个例子, 如果你本来想通过 incr 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了incr ,回滚是没有办法处理这些情况的。
选题思路
redis事务目前在工作跟生产上用的是比较少的一部分,正也是少,很多初学者都无法进行对这一部分进行学习,对初学者比较不友好,不能够全面的去把握redis的使用,看到网上的很多博客对着一块的描述大多一笔带过,让看的人很懵逼,所以我决定写下这个博客帮助后面的人作为一个参考材料去学习巩固自己的技术前性。
写作提纲
采用总-分的结构
1.先总体概述下redis的事务
2.对如何使用redis的事务命令进行总体概述,以及每个命令提供的例子跟具体解读
3.对执行事务过程特殊情况进行分情况(编译型异常,运行时异常)讨论解读,并给出具体例子
4.论述redis和平常的mysql数据库中为什么没有回滚这个功能帮助读者更好的理解redis事务的特殊性。