Redis05-进阶使用(事务)

系列文章目录

Redis01 基础及安装
Redis02-架构及简单使用
Redis03-数据类型:应用及实操
Redis04-进阶使用(管道、发布/订阅)
Redis05-进阶使用(事务)
Redis06-进阶使用(过期 expire)
Redis07-RDB和AOF




Redis的“事务”

首先一点,Redis的事务不像mysql的事务那么完整,它自身没有事务那样可以回滚的操作。那么为什么redis的事务如此粗糙呢?回想一下,还记得我们为什么要用redis吗?对,就是因为Redis快,因此,如果因为某个“东西”让Redis的速度变慢了,还不如不用Redis。

实操

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

在这里插入图片描述

可以看到Redis的事务的命令并不多,下面简单说一下:

  • MULTI 命令用于开启一个事务,它总是返回 OK 。 MULTI 执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC命令被调用时,所有队列中的命令才会被执行。
  • DISCARD , 可以清空事务队列, 并放弃执行事务,客户端会从事务状态中退出。
  • EXEC 命令的返回一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 回复元素的先后顺序和命令发送的先后顺序一致。
  • WATCH 可以实现Redis的CAS。被 监视 的key如果被改动过了(EXEC之前), 那么整个事务都会被取消。
##   实操比较简单
127.0.0.1:6379> MULTI 
OK
127.0.0.1:6379> set k1 aaa
QUEUED
127.0.0.1:6379> set k2 bbb
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
127.0.0.1:6379> keys *
1) "k1"
2) "k2"

注意:多个客户端向redis(单进程、单实例)发起事务时,每个事务的所有指令会被放到一个队列里,等待EXEC指令去执行指令,所以不管哪个客户端先开启事务MULTI,Redis只会先去执行最早发出EXEC指令的那个客户端的事务。
在这里插入图片描述

## 先起一个客户端1
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> get k1
QUEUED
## 等客户端2 exec完了再执行
127.0.0.1:6379> EXEC
1) (nil)

## 再起一个客户端2
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> del k1
QUEUED
## 先执行exec
127.0.0.1:6379> EXEC
1) (integer) 1

客户端1
客户端2

事务中的错误

使用事务时可能会遇上以下两种错误:

  • 事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。
  • 命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。

对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回 QUEUED ,那么入队成功;否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。

不过,从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。

在 Redis 2.6.5 以前, Redis 只执行事务中那些入队成功的命令,而忽略那些入队失败的命令。 而新的处理方式则使得在流水线(pipeline)中包含事务变得简单,因为发送事务和读取事务的回复都只需要和服务器进行一次通讯。

至于那些在 EXEC 命令执行之后所产生的错误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行。

为什么 Redis 不支持回滚(roll back)

  • Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
  • 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。

watch

实操

客户端2在1还没执行exec的时候修改了k2的值,客户端1提交事务的时候返回nil,如下:
客户端1
客户端2

此外,WATCH 命令可以被调用多次。 对key的监视从 WATCH 执行之后开始生效, 直到调用 EXEC 为止。我们还可以在单个 WATCH 命令中监视任意多个key, 就像这样:

WATCH key1 key2 key3

当 EXEC 被调用时, 不管事务是否成功执行, 对所有key的监视都会被取消。
另外, 当客户端断开连接时, 该客户端对键的监视也会被取消。

使用无参数的 UNWATCH 命令可以手动取消对所有键的监视。

使用 WATCH 实现 ZPOP

WATCH 可以用于实现 Redis 的原子操作。

WATCH zset
element = ZRANGE zset 0 0
MULTI
ZREM zset element
EXEC

程序只要重复执行这段代码, 直到 EXEC 的返回值不是nil-reply即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值