multi、exec、discard、watch是Redis中事务的基础。他们允许一组命令在一步内执行完成,并且可以实现如下特点:
1)在事务中的一组命令被序列化并按照顺序追个执行。在事务执行过程中不会出现穿插执行其他客户端请求的问题。这保证了一组命令可以像一个单独的命令一样被执行。
2)事务中的命令要么都被执行要么都不被执行,所以它是原子性的。在事务中exec命令触发事务中所有命令的执行,所以如果客户端在调用multi命令之前丢失了与服务器端的连接,事务中的任何命令都不会被执行,相反,如果exec被调用了,那么事务中所有的命令都将被执行。当设置了appendonly属性为yes时,Redis将会产生一个单独的系统调用将事务写入硬盘中。但是如果Redis服务器突然宕机或者Redis进程被系统管理员杀掉,那么可能会只有一部分操作会被记住,那么Redis在重启的时候检测这种情况,并报告错误并退出。使用redis-check-aof工具可能修复aof文件,它将删除这部分事务操作,Redis就可以正常重启了。
Redis 2.2版本后,除了上面两点外,增加了乐观锁,它非常像check-and-set(CAS)操作。稍后将会介绍这部分内容。
使用范围
Redis事务以命令multi开始,命令总是以ok做响应。这个时候用户可以列出很多的命令,此时Redis将命令放入队列中而不执行它,直到exec命令被调用,所有的命令才会被执行。调用discard命令将会清空事务队列并退出事务。如下是一个简单的使用:
127.0.0.1:6379> multi OK 127.0.0.1:6379> incr foo QUEUED 127.0.0.1:6379> incr bar QUEUED 127.0.0.1:6379> exec 1) (integer) 1 2) (integer) 1 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr foo QUEUED 127.0.0.1:6379> incr bar QUEUED 127.0.0.1:6379> discard OK 127.0.0.1:6379> get foo "1" 127.0.0.1:6379> get bar "1"正如上所见,exec返回了一个数组,其中每个元素是事务中打个命令的响应结果,并且顺序也保持一致。当Redis在事务中的multi环境中,所有的命令均以QUEUE响应,作为Redis协议的响应状态。当exec命令被调用的时候,排队中的命令只是简单的倍调度。
事务中的错误
在事务中可能发生两种类型的命令错误:
1)命令排队失败,在exec调用之前发生错误。如命令语法错误或者处于一种临街状态如超出了内存状态。
2)发生在exec调用之后。如我们执行了一个操作赋值给一个key以错误的类型。如调用一个list赋值操作但是给出一个string的值。
第一类错误是客户端习惯能检测到的。这类错误发生在exec调用之前,通过检查队列中命令的返回值:若命令返回QUEUE,则该命令排队成功,否则Redis将会返回一个错误。若在排队过程中发生错误,客户端将会终止事务并清除队列。
但是在Redis 2.6.5版本之前,客户端将会执行已经排队成功的命令的子集而忽略发生错误的命令。所以新的行为使得很容易通过管道使用事务,所以整个整个的事务将会立刻被发送,同时读取所有的响应。
发生在exec之后的错误并没有做特殊的处理:所有的未发生错误的命令都会被执行,即使在事务中有部分发生错误。
这在协议级别更加的清晰。在下面的例子中将会有一个命令执行失败即使它的语法是正确的:
Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. MULTI +OK SET a 3 abc +QUEUED LPOP a +QUEUED EXEC *2 +OK -ERR Operation against a key holding the wrong kind of valueexec命令返回两个string元素的数组,其中一个是ok状态,而另外一个以一个-ERR响应,这个错误将会被客户端发昂给用户。需要注意的是,在这种情况下,即使有命令失败,其他事物中排队的命令都会被执行而不会终止事务。
接下来是一个展示发生在调用exec之前的错误:
127.0.0.1:6379> multi OK 127.0.0.1:6379> incr a b c (error) ERR wrong number of arguments for 'incr' command这显然是incr语法错误导致的,这个时候Redis响应错误并终止了事务还清空了队列。
通过以上两个例子可以看出,Redis对命令语法的检测只是简单的形式上的、惰性的,只有在命令被执行的时候才会真正的显示出来。
Redis为什么不支持回滚
如果了解过关系型数据库,对于Redis事务发生错误时不回滚的表现会感到非常奇怪。
如下是一些好的观点:
1)Redis只有发生语法错误或者key的相关才做违反了值得数据类型的时候,才会发生错误。也就是说发生在Redis中的命令的失败是一种编程错误、是一种可以在开发过程中发现并修正的错误。
2)由于Redis不需要回滚,所以内部操作是非常简单快速的。
一个关于Redis观点的论据是,发生错误后进行即使按照通常地回滚也不能修复导致这种结果的编程错误。举个例子:要将一个key的值增加2,但是却错误的增加了1或者干脆将key搞错了,在这种情况下,即使回滚也无法避免这种问题,因为这是编码错误,没有办法挽救程序员的编码错误,而且这种错误不应该被带进产品中,所以Redis采取了简单快捷的方法处理,那就是不支持发生错误的回滚。
丢弃事务中的排队队列
discard命令被用来终止事务。一旦调用该命令,事务中的命令都不会被执行,连接状态将回到正常状态。
127.0.0.1:6379> set foo 1 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr foo QUEUED 127.0.0.1:6379> discard OK 127.0.0.1:6379> get foo "1"乐观锁
命令watch被用来提供一个CAS(check-and-set)行为給事务。
watch会监控keys的变化。若在调用exec之前,至少有一个添加了watch的key被修改,整个事务将会被终止,并且exec返回一个空响应通知以表明事务失败。举个例子,假设我们想原子性的实现将key的value增加1(假设Redis中没有incr命令)。
第一次尝试可能如下:
val=get mykey val=val+1 set mykey $val上述实现如果在只有一个客户端的情况下,给一定的时间,其结果是可靠的。但是在多个客户端同时执行如上的操作情况下,其结果将会是一个不确定的。有了watch命令,我们可以很好的解决这个问题:
watch mykey val=get mykey val=val+1 multi set key $value exec以上代码中,我们watch了mykey,并且将set操作放在事务中,如果在我们调用exec之前mykey的value被改变了,watch会终止后面的执行,这样客户端就知道操作成功与否。
关于watch的解释
watch是一个命令,它将exec命令编程一个条件语句:只有在我们watch的keys没有一个被修改,Redis才会执行事务,否则事务根本不会发生。例外情况是,这个key设置了过期时间并且已经到了时间,那么对这个key的watch将失效,exec是会被执行的。
watch可以使用多次。watch发生作用的是从调用该命令开始直到exec被执行。也可以使用一个watch监控多个命令。
调用exec之后,所有的keys的watch状态失效,处于unwatched状态,在不管事务成功与否的情况下。当客户端失去连接后,所有的keys都会变成unwatched状态。
unwatch命令可以取消对keys的watch监控。这在我们使用乐观锁锁住少数keys的情况下非常有用,因为我们很可能想通过事务修改这些keys,但是读取了它们的values后我们不想这么做了。
使用watch命令实现zpop
有一个解释watch可以方便的创造一个原子操作的很好例子是zpop的实现。zpop定义为原子性地从sorted sets中移除其score最小的元素。下面是一个实现:
watch zset element=zrange zset 0 0 multi zrem zset element exec如果exec执行失败,我们只需要重复以上操作。
Redis脚本化和事务
Redis 脚本是定义好的事务性的,支持原子操作。所以可以用事务做的一切工作都可以用脚本实现,而且又是在于,通常脚本更加地简介快速。
脚本化和事务看似是一个重复操作,其实不然。首先,Redis脚本是Redis 2.6才有的特性而事务一开始就存在。但是不像移除事务的原因是它具有很好的语义并且在没有使用Redis脚本的情况下也可以工作的很好,尤其事务的实现复杂性是最小的。
或许在不久的将来,我们将会看到用户只是使用脚本而不再青睐事务,这是我们也将反对事务转而支持脚本化,从而将事务从Redis中移除。
相关推荐
本文将深入探讨“SpringBoot-Redis事务”这一主题,旨在帮助开发者理解如何在SpringBoot项目中利用Redis进行事务操作。 首先,SpringBoot是Spring框架的一种快速启动方式,它简化了配置,提供了自动配置的功能,...
【Redis 事务与关系型数据库事务的比较】 Redis 和关系型数据库(如 MySQL)在事务处理上有显著的差异。在Redis中,事务提供了一种批量执行命令的方式,以确保原子性,但其机制与传统的ACID(原子性、一致性、隔离...
Redis 事务是数据库系统中的一种特性,允许用户一次性提交...例如,Redis事务不支持回滚操作,如果事务中的某条命令失败,后续命令仍会执行。因此,在设计使用Redis的事务时,需要考虑到这些限制并进行适当的错误处理。
基于SpringBoot的轻量级redis事务回滚机制,使用栈和ThreadLocal记录业务链的redis操作,发生异常进行回滚,参考了阿里巴巴Seata AT模式的db回滚策略:补偿回滚,记录前镜像与当前操作语句,反向解析生成补偿动作。...
Redis 事务(Transactions)是 Redis 数据库提供的一种批量执行命令的功能,尽管它的事务机制相比于传统的关系型数据库事务较为简化,但仍能满足一些基本的事务需求。Redis 事务的主要特点在于其简单、快速和高效,...
下面是对 Redis 的常用命令、配置文件、持久化、事务、主从复制、Jedis 使用的详细讲解。 Redis 常用命令 Redis 提供了很多有用的命令来管理和操作数据。下面是一些常用的 Redis 命令: * SET key value:设置...
【Redis事务】 Redis事务是Redis数据库中提供的一种确保数据操作原子性的机制,它允许用户在一个操作序列中执行多条命令,这些命令会被串行化并按顺序执行,以保证在并发环境下的数据一致性。事务的开启是通过`MULTI...
然而,事务处理并非总是顺利进行,"redis事务处理:(error) EXECABORT" 是一个常见的错误提示,意味着在执行事务时遇到了问题。本文将深入探讨这个错误的原因以及如何解决。 一、Redis 事务基础 Redis 的事务机制...
Redis 事务是 Redis 数据库提供的一种批量执行命令的机制,旨在提高数据操作的效率和原子性。虽然在传统的数据库系统中,事务具有ACID(原子性、一致性、隔离性和持久性)特性,但 Redis 事务并不完全具备这些特性。...
一、Redis事务简介 Redis事务可以一次执行多个命令,一个事务的所有命令都会序列化并按顺序地串行化执行,而不会被其他客户端提交的命令请求插入到事务执行命令序列中。 二、Redis事务命令 下面的表格节选自:...
Redis 事务(上).flv
在Redis中实现事务主要依靠以下几个命令来实现:Redis事务从开始到结束通常会通过三个阶段:1.事务开始2.命令入队3.事务执行以下是一个最简单的Redis事务流程:第一步跟其他的关系型数据库类似,也是需要开启一个事务...
一、Redis 事务的实现原理 一个事务从开始到结束通常会经历以下三个阶段: 1、事务开始 客户端发送 MULTI 命令,服务器执行 MULTI 命令逻辑。 服务器会在客户端状态(redisClient)的 flags 属性打开 REDIS_MULTI ...
redis 的并发竞争问题是什么?如何解决这个问题?了解 redis 事务的 CAS 方案吗?
本篇文章将详细探讨如何使用Redisson实现Redis分布式事务锁,以及在Spring Boot环境中如何进行集成。 首先,Redis作为一个内存数据库,其高速读写性能使其成为实现分布式锁的理想选择。分布式锁的主要作用是在多...
Redis事务和CAS(Check-And-Set)机制是Redis中用于处理并发操作的重要特性,确保数据的一致性和完整性。在高并发的网络活动中,如证券交易,这类机制是必不可少的。 Redis事务通过`MULTI`, `EXEC`, `DISCARD`, 和 ...
本文主要介绍了Redis事务的概念、Redis不保证原子性、Redis事务的三个阶段、Redis事务相关命令以及Redis事务使用案例。 本文来自于博客园,由火龙果软件Anna编辑、推荐。Redis事务的本质是一组命令的集合。事务支持...