redis(4)事务
一、事务
一般来说,事务必须满足4个条件,也就是我们常说的ACID:
1)Atomicity 原子性:一个事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间的某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像是这个事务从来没有执行一样。
2)Consistency 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须符合所有预设原色,者包含资料的精确度、串联性以及后续的数据库可以自发性地完成预定工作。
3)Isolation 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交、读提交、可重复读和串行化。
4)Durability 持久性:事务处理以后,对数据的修改就是永久的,即便系统故障也不会丢失。
二、redis事务
从本质上来说,redis的事务其实不能算是事务,或者说它不遵循ACID原则,只是将一组命令置入队列,然后像执行一个命令一样执行一组命令。可是它并不保证所有命令完整执行,也不支持事务回滚。因此,当一个事务中其中一个命令发生错误,其它命令还是会被执行。这听起来有些奇怪,不过redis给出了相应的解释。后面我们会提到。
redis事务有两个原则:
1)一个事务包含一组命令,当事务在执行期间不会有其它客户端的命令穿插执行。这意味着,这组命令在执行期间类似于一个隔离的命令操作。
2)事务通过multi命令创建,multi之后的命令将会进入执行队列,当exec命令执行的时候,会将进入队列的命令全部按顺序执行,并返回顺序的结果。这里要注意:1)如果redis在multi执行之前发生如硬件崩溃等错误,那么所有东西都不会被执行。2)如果redis在exec执行之后发生崩溃等错误,那么所有都洗都会被执行。3)当使用AOF持久化策略的时候,会将事务写入日志文件,如果写入过程发生崩溃等错误,那么redis重新启动加载日志的时候会发生错误并退出(可以使用redis-check-aof)工具解决。
三、使用
我们看一个简单的示例
127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 23 QUEUED 127.0.0.1:6379> set name lay QUEUED 127.0.0.1:6379> exec 1) OK 2) OK
multi: 开启一个事务
set age 23 命令入队
set name lay 命令入队
exec: 事务执行
返回数据结果
四、错误
在一个事务开始到执行结束期间可能发生很多问题,主要分为如下两种:
1)exec执行之前发生错误,如发生语法错误。
2)exec执行期间发生错误,如操作的值类型错误。
一般上,如果exec执行之前发生错误,那么客户端会将当前的事务取消、队列的命令清除。如果在exec执行期间发生错误,那么其它命令还会继续执行,并不会回滚。
五、为什么redis不支持事务回滚?
熟悉关系型数据库的你应该很清楚事务回滚,当看到redis不支持回滚肯定会觉得很奇怪。redis官方给出解释原文如下:
- Redis commands can fail only if called with a wrong syntax (and the problem is not detectable during the command queueing), or against keys holding the wrong data type: this means that in practical terms a failing command is the result of a programming errors, and a kind of error that is very likely to be detected during development, and not in production.
- Redis is internally simplified and faster because it does not need the ability to roll back.
主要是两点:
1)redis官方认为,导致redis出错的情况一般是在开发期间就可以发现的,如语法错误,操作的值类型不对。而不应当把研发上的错误在生产环境里检查,而真的在生产环境出现的错误,其实通常是事务回滚也无法解决的。
2)事务带来的开销是性能的损失,redis本身是单线程架构,追求内存的高效,所以事务回滚并不符合它的宗旨。
总结一下:redis容许发生一些影响不大的错误(如空值什么的),虽然产生使用上的限制,但减少了开销,提高了性能。
六、事务执行条件
redis中有一个watch命令,它可以监听key的变化,如果事务在执行之前key的值由其它客户端改变,那么当前事务将会被取消、队列命令也会被清除。
如:
client1开启事务:
127.0.0.1:6379> watch age OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 25 QUEUED
client2改变数据:
127.0.0.1:6379> set age 27 OK
client1执行exec:
127.0.0.1:6379> exec (nil) 127.0.0.1:6379>
返回nil,当前事务已经被取消了
七、事务命令
discard: 取消
exec: 执行
multi: 开启
unwatch: 取消监听
watch: 监听
详细参考官方文档:https://redis.io/topics/transactions
八、lua
redis内嵌了lua模块,这里简单提一下lua的执行也是事务性的,在redis中可以用lua来实现事务,并且通常lua更加的简单和快速。