事务:是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元);
事务的四大特性:
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
一个事务从开始到执行会经历以下三个阶段:
开始事务。
命令入队。
执行事务。
以上是redis中文文档对事务的描述,但是基本上都是废话,还有些歧义,大家姑且看看就行了,真正权威又详细的解释如下: redis官网
官网虽然写的挺详细但是是全英文的,看不懂的童鞋和我一起解密redis事务。
在理解redis事务之前,我们亲自试一下比较容易理解:
127.0.0.1:6379> set k1 20 //先设置一个变量
OK
127.0.0.1:6379> multi //multi命令开启事务
OK
127.0.0.1:6379> set k2 100 //设置变量k2
QUEUED //命令不会立刻执行,而是进行入栈操作
127.0.0.1:6379> incr k1 //事务中可以操作redis的任意变量
QUEUED //命令入栈
127.0.0.1:6379> decrby k2 20
QUEUED //命令入栈
127.0.0.1:6379> get k1
QUEUED //命令入栈
127.0.0.1:6379> get k2
QUEUED //命令入栈
127.0.0.1:6379> exec //exec触发时,才真正开始执行事务
1) OK
2) (integer) 21
3) (integer) 80
4) "21"
5) "80" //exec会依次执行栈中命令,并依次返回结果
看了以上的实践,我们可以做出总结:
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总的说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
watch key1 key2 … : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
multi : 标记一个事务块的开始( queued )
exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
discard : 取消事务,放弃事务块中的所有命令
unwatch : 取消watch对所有key的监控
批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。所以,redis的一个事务不可能被其他事务所干扰,你可以理解成redis事务没有隔离级别的概念,也可以说redis事务具有隔离性。
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务操作可能会遇到以下两种错误:
127.0.0.1:6379> set k1 20
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k2 100
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> decr k2 20 //错误的命令
(error) ERR wrong number of arguments for 'decr' command
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec //全部不会执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k3 string
QUEUED
127.0.0.1:6379> decr k3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) "string"
根据第二种情况,我们完全可以确定redis事务是不保证原子性的。
redis事务与传统数据库事务类似,但是有两点区别:
在事务运行期间,虽然Redis命令可能会执行失败,但是Redis仍然会执行事务中余下的其他命令,而不会执行回滚操作,你可能会觉得这种行为很奇怪。然而,这种行为也有其合理之处:
只有当被调用的Redis命令有语法错误时,这条命令才会执行失败(在将这个命令放入事务队列期间,Redis能够发现此类问题),或者对某个键执行不符合其数据类型的操作:实际上,这就意味着只有程序错误才会导致Redis命令执行失败,这种错误很有可能在程序开发期间发现,一般很少在生产环境发现。
Redis已经在系统内部进行功能简化,这样可以确保更快的运行速度,因为Redis不需要事务回滚的能力。
对于Redis事务的这种行为,有一个普遍的反对观点,那就是程序有可能会有缺陷(bug)。但是,你应当注意到:事务回滚并不能解决任何程序错误。例如,如果某个查询会将一个键的值递增2,而不是1,或者递增错误的键,那么事务回滚机制是没有办法解决这些程序问题的。请注意,没有人能解决程序员自己的错误,这种错误可能会导致Redis命令执行失败。正因为这些程序错误不大可能会进入生产环境,所以我们在开发Redis时选用更加简单和快速的方法,没有实现错误回滚的功能。