一文了解:Redis事务
redis事务
事务提供了一种"将多个命令打包,一次性提交并按顺序执行"的机制,提交后在事务执行中不会中断。只有在执行完所有命令后才会继续执行来自其他客户的消息。
redis中的使用
redis通过multi,exec,discard,watch实现事务功能。
- multi:开始事务
- exec:提交事务并执行
- 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"
设置监控letter键,客户端1进入事务,设置letter的value为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 深度历险:核心原理与应用实践》
欢迎关注公众号:bate喵
专注于java相关技术:linux、jvm、springcloud、springboot、docker、redis、netty、微服务、高并发等领域,偶尔聊聊学习之道、自控力等方法,致力于成就更好的自己,欢迎大家关注交流,共同成长