21

Redis 中的事务

 3 years ago
source link: http://www.cnblogs.com/54chensongxia/p/13787247.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Redis 事务简介

稍微了解 Redis 的朋友都知道,Redis 也提供了事务功能。但是 Redis 的事务和我们平时熟悉的关系型数据库中的事务是有区别的。

7feYVza.jpg!mobile

Redis 事务的本质是一组命令的集合:一个事务中所有命令都会被序列化到一个队列中,在事务执行过程,会按照顺序串行执行队列中的命令,这些命令要么全部得到执行,要么全部不执行。另外,其他客户端提交的命令请求不会插入到事务执行命令序列中。

Redis 事务的使用

Redis 事务的基础是 MULTIEXECDISCARDWATCH 等命令。

这些命令的使用方式如下:

# 开启事务
MULTI
command1
command2
# 触发事务
EXEC

MULTI 命令用于开启一个事务,它总是返回 OKMULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中。

EXEC 命令负责触发并执行事务中的所有命令, EXEC 命令的回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 其中, 回复元素的先后顺序和命令发送的先后顺序一致。

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

另外,需要注意的是:如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。

Redis 事务对错误的处理

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

maxmemory

对于在执行 EXEC 之前发生的错误,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。

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

对于第二种情况,敏感的朋友会问:Redis 发现错误时会不会回滚已经执行成功的操作呢?答案是不会。

Redis 给出的理由是:

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

如果我们的程序中确实需要对事务进行回滚,可以结合使用 Lua 脚本来实现事务。简单的做法是在事务开始前记录状态,如果在程序执行过程中发生了异常错误,将被修改的数据恢复到之前即可。

watch 命令的使用

WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。

被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回空来表示事务已经失败。

当发生被监控的键被改变时,程序需要做的就是不断重试这个操作, 直到没有发生碰撞为止。

下面我们来演示下 watch 命令的使用:

127.0.0.1:6379> set csx:key:1 test
OK
127.0.0.1:6379> watch csx:key:1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> ping message1
QUEUED
127.0.0.1:6379> ping message2
QUEUED
# 在执行 exec 之前,使用另外一个客户端将csx:key:1这个键的值修改了
127.0.0.1:6379> exec
(nil)

WATCH 使得 EXEC 命令需要有条件地执行: 事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行。

如果你使用 WATCH 监视了一个带过期时间的键, 那么即使这个键过期了, 事务仍然可以正常执行。

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

使用无参数的 UNWATCH 命令可以手动取消对所有键的监视。 对于一些需要改动多个键的事务, 有时候程序需要同时对多个键进行加锁, 然后检查这些键的当前值是否符合程序的要求。 当值达不到要求时, 就可以使用 UNWATCH 命令来取消目前对键的监视, 中途放弃这个事务, 并等待事务的下次尝试。

一个问题

当 Redis 客户端开启了事务,并且也输入了几个命名,但是客户端因为某些原因断开了。那么 Redis 服务端是怎么处理那些已经进入队列的命令的?

参考


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK