Redis | Redis 的事务一

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis | Redis 的事务一

       对于关系型数据库而言,事务是很重要的功能,数据库的事务在执行时具备 ACID 四种属性,即 原子性、一致性、隔离性 和 持久性。在 Redis 中同样也有事务的功能,我整理了 Redis 关于事务的命令和一些简单的说明,让我们看看 Redis 的事务。

命令介绍

       Redis 关于事务的命令只有简单的几个,如下图:

       可以看到 Redis 关于事务的命令只有 5 条。下面来分别介绍一下这几条命令。

multi:开始事务;

exec:提交事务;


discard:取消事务;


watch:监视某个 key;


unwatch:取消监视某个 key。


       Redis 关于事务的命令就这么几个,基本上常用的就是 multi、exec 和 watch 这三个命令。我们来分别介绍一下这几个命令。


命令的使用

       我们先来简单的看一个例子,主要来了解一下 multi 和 exec 的使用。我们构建一个场景,首先商店的 tshirt 有 10 件,张三的钱有 1000 元,张三有 tshirt 共 0 件,我们以此来初始化环境,命令如下:

127.0.0.1:6379> set tshirt 10
OK
127.0.0.1:6379> set zhang:money 1000
OK
127.0.0.1:6379> set zhang:tshirt 0
OK

       当张三购买 tshirt 的时候,会发生三个事情,首先是 tshirt 要减库存,然后张三的钱要减少,最后张三的 tshirt 会多一件。有这么一个过程,这个过程要么都完成,要么都不完成。

也就是说,张三不能钱减少了,tshirt 却没增加也不能是库存减少了,而张三的钱没减少。基本就是这么一个过程。

       那么,当我们执行命令时,要么同时完成三个操作,要么这三个操作一个也不完成,这就是所谓的原子性。而提到原子性,就离不开事务。我们使用 Redis 来完成上面的步骤。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr tshirt
QUEUED
127.0.0.1:6379> decrby zhang:money 100
QUEUED
127.0.0.1:6379> incr zhang:tshirt
QUEUED
127.0.0.1:6379> exec
1) (integer) 9
2) (integer) 900
3) (integer) 1

      当我们通过 multi 开启一个事务后,multi 之后,exec 之前的命令是不会马上执行的。通过命令返回的 QUEUED 可以看出,multi 之后的命令都保存到了一个队列之中。当输入命令 exec 之后,在队列中的命令会一次性的执行。在提交了 exec 命令后的返回中可以看出,tshirt 减了 1,zhang:money 减少了 100,zhang:tshirt 增加了 1,顺利的完成了一次简单的交易操作。


       目前只有一个客户端在购买 tshirt,如果是两个客户会怎样呢?我们通过 flushdb 先清空一下 Redis,再重新初始化一下我们的环境,命令如下:

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set tshirt 1
OK
127.0.0.1:6379> set zhang:money 1000
OK
127.0.0.1:6379> set zhang:tshirt 0
OK
127.0.0.1:6379> set li:money 1000
OK
127.0.0.1:6379> set li:tshirt 0
OK

       这次,分别有了 zhang 和 li 两个人,库存的 tshirt 只有一件,然后我们让他们分别来买这仅有的 1 件 tshirt。zhang 和 li 在两个不同的命令行窗口中操作,命令如下:

get tshirt

       zhang 和 li 都分别执行上面的命令,来查看 tshirt 的剩余数量,两人都同时发现还有一件 tshirt。然后两个人开始下单。先来看 zhang 的命令,命令如下:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr tshirt
QUEUED
127.0.0.1:6379> decrby zhang:money 100
QUEUED
127.0.0.1:6379> incr zhang:tshirt
QUEUED

     上面的命令是 zhang 支付后的命令,接着是 li 支付后的命令,在另外一个命令行窗口输入命令如下:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr tshirt
QUEUED
127.0.0.1:6379> decrby li:money 100
QUEUED
127.0.0.1:6379> incr li:tshirt
QUEUED
127.0.0.1:6379> exec
1) (integer) 0
2) (integer) 900
3) (integer) 1

      接着回刚才 zhang 的命令行窗口输入 exec 命令,看结果,结果如下:

127.0.0.1:6379> exec
1) (integer) -1
2) (integer) 900
3) (integer) 1

       可以看到,执行 exec 后,tshirt 成为了 -1,相应的 zhang:money 和 zhang:tshirt 也更新了。虽然执行是没问题,但是逻辑上错了。tshirt 库存成为了 -1,那就相当于是超卖了。这样就产生了问题。

       在解决这个问题之前,我们把上面的内容梳理一下。Redis 的事务支持 原子性 和 隔离性,当事务开始执行时,事务队列中的命令会一次性执行完成,不会被其他的命令打断,从而可以它拥有原子性;当我们对一个 key 进行修改操作时,另外一个客户端也对 key 进行了修改操作,这样对我们的修改操作就产生了影响,那么看起来就不具备隔离性了,但是 Redis 提供了 watch 命令,我们可以通过 watch 命令来保证其隔离性(但是上面的测试中,没有使用 watch,因此也就没有体现出隔离性)。

       Redis 的事务不支持回滚,当事务开始执行时(即执行了 exec 命令),事务就会将所有的命令执行完成,除非在 multi 命令后错误的输入了一条不存在的命令,此时执行 exec 命令时不会执行事务中的命令,如下所示:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr tshirt
QUEUED
127.0.0.1:6379> gets tshirt
(error) ERR unknown command `gets`, with args beginning with: `tshirt`, 
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.

    可以看到,在上面的命令中,输入了 gets,而 gets 并不是 Redis 的命令,因此 gets 并没有入队,当执行 exec 后,整个命令也没有被执行。


       除了上面的问题外,当 Redis 的事务中有错误的命令使用,Redis 会执行所有的命令,如下:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr tshirt
QUEUED
127.0.0.1:6379> sadd zhang:money 100
QUEUED
127.0.0.1:6379> incr tshirt
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) (integer) 2

      上面的命令中,使用 sadd 对字符串进行操作显然是错误的,但是在输入完命令后,命令入队了,因为在检查命令语法时命令的使用格式是正确的,只有实际执行时才会发现问题所在,这就类似我们写代码时的编译时错误和运行时错误类似。因此,第一条命令执行成功了,第三条命令也执行成功了。为什么后面 sadd 后面的指令能成功呢?因为这是程序员造成的问题,和 Redis 本身没有太多的关系,就像我们写的程序,在运行时逻辑出错,编译器是帮我们检查不出来的。那么 Redis 为什么不进行回滚呢?因为 Redis 的设计初衷就是为了快,如果加入回滚的功能,那么必定就会影响 Redis 的效率。对于不同的工具都具备其不同的设计考虑,一味的求全,而丧失了其设计本意,那么可能就是失败的设计了。

总结      

Redis 的提供了对事务的支持,由于 Redis 本身的特性,因此对于事务的支持较弱,它不支持回滚。


       Redis 通过 multi 命令可以让其后续的命令进入队列,当执行 exec 命令时,队列中的命令一次性执行完成,保证了原子性。当使用了不正确的命令操作 key 时,在执行该命令时会报错,这样可能会影响到数据的一致性,但是只要对 Redis 的命令使用正确就可以保证一致性了。因为事务中修改的 key 可能被其他的客户端进行修改,因此,无法保证隔离性,但是 Redis 提供了 watch 命令,通过 watch 命令监控当前要修改的 key 是否被修改,就可以保证事务的隔离性。至于持久性,Redis 本身就是当作缓存在用,那么其持久性是否能够保证呢?那就看 Redis 的使用场景和整个项目的设计了。当多个客户端发起事务时,哪个客户端先通过 exec 进行提交,那么就先执行那个客户端的事务。


       关于 watch 的使用,在下一篇文章中进行整理。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
25天前
|
NoSQL Redis 数据库
什么是Redis的事务?
Redis事务提供原子性和顺序性,确保命令按顺序执行且不被打断。核心概念包括原子性、顺序性、隔离性和持久性。关键指令有MULTI、EXEC、DISCARD和WATCH,用于事务的开始、执行、取消和监视。这保障了命令的完整性,防止并发操作导致的数据不一致。
13 2
|
2月前
|
缓存 NoSQL 数据处理
Redis事务悄然而至:命令的背后故事
Redis事务悄然而至:命令的背后故事
27 0
|
3月前
|
缓存 监控 NoSQL
Redis之事务
【1月更文挑战第7天】Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!一次性、顺序性、排他性!执行一些列的命令。
169 1
|
3月前
|
消息中间件 移动开发 NoSQL
Redis 协议 事务 发布订阅 异步连接
Redis 协议 事务 发布订阅 异步连接
|
3月前
|
缓存 监控 NoSQL
Redis之事务
Redis之事务
|
4月前
|
监控 NoSQL 关系型数据库
|
25天前
|
NoSQL Redis
Redis事务:保证数据操作的一致性和可靠性
Redis事务:保证数据操作的一致性和可靠性
|
5月前
|
监控 NoSQL Redis
【Redis 系列】redis 学习六,redis 事务处理和监控事务
【Redis 系列】redis 学习六,redis 事务处理和监控事务
|
4月前
|
监控 NoSQL Java
Redis数据库 | 事务、持久化
Redis数据库 | 事务、持久化
28 0
|
2月前
|
消息中间件 NoSQL Kafka
Redis事务与异步方式
Redis事务与异步方式
35 0

热门文章

最新文章