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

redis 刘宇帅 6年前 阅读量: 1228

事务

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 中的脚本本身就是一种事务,所以任何在事务里可以完成的事,在脚本里面也能完成。并且一般来说,使用脚本要来得更简单,并且速度更快。

提示

功能待开通!


暂无评论~