什么是redis?
Redis 是一个开源的,内存中的数据结构存储系统。
可以用作数据库、缓存和消息中间件。
什么是事务?
事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;
这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;
事务是一组不可再分割的操作集合(工作逻辑单元);
顺带提一下事务的四个特性:
A:原子性(Atomicity)
事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
C:一致性(Consistency)
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。
如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。
I:隔离性(Isolation)
一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
D:持久性(Durability)
也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
什么是Redis事务?
官网上给出的解释是:
事务可以一次执行多个命令,并且带有以下两个重要的保证:
1.事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
2.事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
EXEC 命令负责触发并执行事务中的所有命令:
如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。
另一方面,如果客户端成功在开启事务之后执行 EXEC ,那么事务中的所有命令都会被执行。
这样看,好像和事务的解释是一致的,那么redis事务应该能满足事务的四个特性。
那么我们实际操作试一下。
第一组命令:
multi
incr foo
incr boo
exec
执行结果
第二组命令:
multi
incr foo
asdasd
incr boo
exec
执行结果
(Another Redis客户端↓)
(Redis Desktop Manager客户端↓)
可以看出,在第三条命令中我随便打了几个字符,提交事务的时候并没有成功,符合我们对事务的理解,具有原子性。但是,有一个细节,那就是错误命令在我输入的时候就已经报错了(这块Another Redis客户端与Redis Desktop Manager客户端表现不一样,但结果是一样的,这个后续再说),也就是说这条错误命令在进入队列的时候redis就已经知道这是一条错误命令,这样,整个事务的命令将全部失败。
那么如果命令是正确的,但在执行时报错呢?
multi
set foo a
incr foo
incr boo
exec
执行结果
(foo是字符串类型,再执行incr命令肯定不成功)
那么综上,我们在使用redis事务时可能会遇上以下两种错误:
1.事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。
2.命令可能在 EXEC 调用之后失败。例如事务中的命令可能处理了错误类型的键,将列表命令用在了字符串键上面,诸如此类。
对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回 QUEUED ,那么入队成功;否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。(这相当于解释了两个客户端,对同一个错误语句,返回结果不同,说明是客户端自己控制的。)
下面这应该能是加分项
不过,从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。在 Redis 2.6.5 以前, Redis 只执行事务中那些入队成功的命令,而忽略那些入队失败的命令。
那么回过头来看一下,Redis事务有没有满足事务的四个特性?
好像是没有。
那没有满足哪条呢?
有人可能说是原子性,但原子性的描述是“事务中包含的各操作要么都做,要么都不做”;不是“事务中包含的各操作要么都成功,要么都不做”。那么redis事务中的各项操作是不是都执行了?是。符合原子性描述吗?符合。只是有的没成功。那么没成功执行的操作结果也存在了数据库中,影响了什么?影响了事务的一致性。
也就是redis没有保证使数据库从一个一致性状态变到另一个一致性状态,也就是数据中不仅包含成功的结果,同时包含了失败的结果。
那么导致redis坚持了原子性,损害了一致性的问题根源在哪呢?
在于redis没有回滚这个东西。
为什么 Redis 不支持回滚(roll back)
官网给出的解释:
以下是这种做法的优点:
Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。
综上,我们需要记住的是两点:
1.即使事务中有某条/某些命令执行失败了, 事务队列中的其他命令仍然会继续执行 —— Redis 不会停止执行事务中的命令。
2. Redis 不支持回滚
完。