redis 7年前

从零学习redis(10)--- 事务

作者头像 刘宇帅
2563 0

事务

redis 中和事务相关的命令有四个:MULTI EXEC DISCARD WATCHMULTI 用于开始一个事务,EXEC 用于提交事务,DISCARD WATCH 可以为事务提供乐观锁(后面会讲具体使用)。
事务的流程:客户端连接服务器,MULTI 命令开启一个事务,然后客户端可以向服务器发送任意多条命令,但是命令不会立即执行,直到我们发送EXEC命令,所有命令才会根据发送的顺序依次执行。当我们在执行EXEC之前如果想要停止事务可以通过发送DISCARD来终止事务。
redis 事务有如下特性:

  • 事务是一个隔离操作,事务中的命令会按顺序执行,事务执行过程中不会被其他客户端打断。
  • 事务是一个原子操作,事务中的命令要不全部执行,要不都不执行。
  • 如果事务执行MULTI之后,客户端因为断线等断开连接而没有执行EXEC命令,那么事务中的所有命令都不会被执行。

操作展示

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set test1 1
QUEUED
127.0.0.1:6379> set test2 2
QUEUED
127.0.0.1:6379> set test3 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK

由示例我们可以看到:MULTI 命令开始一个事务返回值是 ok,之后发送的命令的返回值均为 QUEUED ,执行 EXEC 提交事务的返回值是事务中包含的各个命令的返回值,返回值的顺序和发送命令的顺序一致。
展示DISCARD放弃事务

127.0.0.1:6379> get hello1
"1"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set hello1 2
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> get hello1
"1"

事务中的错误

事务中包含两种错误:

  • 事务在执行EXEC之前,比如入队的命令格式错误,服务器内存不足等
  • 调用EXEC之后,比如事务中的命令处理了错误类型的键等

第一种:当发送命令存在错误的时候,服务直接返回错误并终止该次事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr a bd
(error) ERR wrong number of arguments for 'incr' command
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.

第二种:当错误命令入队的时候服务器只是记录错误的命令,只是在EXEC执行后,不执行该命令并返回错误,正常执行其他命令。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set hello1 1 1
QUEUED
127.0.0.1:6379> set hello2 2
QUEUED
127.0.0.1:6379> exec
1) (error) ERR syntax error
2) OK

set hello1 1 1 格式错误,命令入队的时候没有什么特殊只是在执行阶段返回错误。

redis 为什么不支持回滚

原因如下

  • Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
  • 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
  • 在通常情况下,回滚并不能解决编程错误带来的问题。举个例子,如果你本来想通过 INCR 命令将键的值加上 1 ,却不小心加上了 2 ,又或者对错误类型的键执行了 INCR 回滚是没有办法处理这些情况的。

WATCH

WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。
假如我们需要在执行一个事务的时候需要保证另外一个或多个key必须不变,否则就放弃这个事务我们。

127.0.0.1:6379> watch hello1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set hello1 2
QUEUED
127.0.0.1:6379> exec
1) OK

如果在WATCHEXEC之间有其他的客户端修改了 hello1 的值那么 exec 将返回 nil,示例如下

127.0.0.1:6379> watch hello1  // 之后我们用另外一个客户端修改 hello1 的值
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set hello1 2
QUEUED
127.0.0.1:6379> set hello2 2
QUEUED
127.0.0.1:6379> set hello3 3
QUEUED
127.0.0.1:6379> exec
(nil)

可以多次执行WATCH 监视多个值,对值的监视是从WATCH 执行后开始的,执行EXEC时,不管事务是否成功,都会放弃对所有 key 的监视。UNWATCH 用于手动取消对所有 key 的监视。

持久化

当使用 AOF 方式做持久化的时候, Redis 会使用单个 write(2) 命令将事务写入到磁盘中。
然而,如果 Redis 服务器因为某些原因被管理员杀死,或者遇上某种硬件故障,那么可能只有部分事务命令会被成功写入到磁盘中。
如果 Redis 在重新启动时发现 AOF 文件出了这样的问题,那么它会退出,并汇报一个错误。
使用redis-check-aof程序可以修复这一问题:它会移除 AOF 文件中不完整事务的信息,确保服务器可以顺利启动。

脚本和事务

从定义上来说,Redis 中的脚本本身就是一种事务,所以任何在事务里可以完成的事,在脚本里面也能完成。并且一般来说,使用脚本要来得更简单,并且速度更快。

作者头像

刘宇帅

非著名程序员,全栈开发工程师,长期专注系统开发与架构设计。

提示

功能待开通!


暂无评论~

相关文章

从零学习redis(8)--- 过期及过期策略

redis 的 string 类型是支持过期设置的,默认是永不过期的。 Redis 设置过期 redis 中设置设置 key 过期有3中方式 第一种在设置值的时候指定过期时间 Set 命令格式 SET key value [EX seconds] [PX milliseconds] [NX|XX] EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。 PX millisecond :设置键的过期时间为 millisecond 毫秒。 3. SET key value PX m

从零学习redis(6)--- 多机部署之集群模式

Redis 作为高效的缓存数据库,单机也能够表现出很好的性能。但是随着数据的增加、并发的增加,单机模式的 Redis 会受到单机性能、容量、稳定性的限制,即使 Redis 提供了稳定的持久化方案,但是单机服务器始终是部署在单个机器上,如果运行机器本身的服务器出现问题我们很难在短时间恢复服务。所以 Redis 提供了三种多机模式: 主从复制模式 哨兵模式 集群模式 redis集群模式 哨兵模式解决了主节点挂掉的问题,但是没有解决从节点挂掉的问题,而 redis 集群模式可以有效的解决这个问题。redis集群中的数据是和槽(slot)挂钩的,一共定义了16384个槽,所有的数据使用一致性哈希分

从零学习redis(5)--- 多机部署之哨兵模式

Redis 作为高效的缓存数据库,单机也能够表现出很好的性能。但是随着数据的增加、并发的增加,单机模式的 Redis 会受到单机性能、容量、稳定性的限制,即使 Redis 提供了稳定的持久化方案,但是单机服务器始终是部署在单个机器上,如果运行机器本身的服务器出现问题我们很难在短时间恢复服务。所以 Redis 提供了三种多机模式: 主从复制模式 哨兵模式 集群模式 哨兵模式 主从复制模式解决了数据备份和单机可能存在的性能问题,但是也引入了新的问题,一主多从,在使用过程中,如果主服务器挂掉那么整个 Redis 服务的写就挂掉了,如果一个从服务器挂掉那么调用方就无法正确的读数据了,只能通过修改调

从零学习redis(15)--- 源码阅读之内存分配

redis 中内存管理没有直接使用 C 语言提供的方法而是做了一个可管理已分配内存大小及添加了自己分配策略的实现,下面从 SDS 开始去了解 redis 的内存管理实现及策略。 SDS 内存管理函数定义如下: src/sdsalloc.h #include "zmalloc.h" #define s_malloc zmalloc #define s_realloc zrealloc #define s_free zfree s_malloc 内存申请、s_realloc 内存重新分配、s_free 内存释放,这里定义分别是 zmalloc.h 里三个函数的别名,而 zmalloc zrea

从零学习redis(12)--- 大量导入数据

有时候我们需要在短时间内往 redis 里插入大量的数据,我们如果单条单条的出入会浪费很多时间和服务器资源,我们可以使用管道来实现快速的插入。 netcat 我们把需要插入 redis 的数据创建为如下的一个命令集文件 set key1 val1 set key2 val2 ... set key3 val3 我们使用如下命令执行数据导入 > $ cat /tmp/test|nc localhost 6379 +OK +OK +OK pipe mode 使用 netcat 并不是一个可靠地方式,因为用netcat进行大规模插入时不能检查错误。从Redis 2.6开始redis-cli支