redis(11):事务

关系型数据库中有事务的概念,redis一样也有;


一、概念

Redis中的事务(transaction)是一组命令的集合。

事务同命令一样都是 Redis 的最小执 行单位,一个事务中的命令要么都执行,要么都不执行。事务的应用非常普遍,如银行转账 过程中A给B汇款,首先系统从A的账户中将钱划走,然后向B的账户增加相应的金额。这两 个步骤必须属于同一个事务,要么全执行,要么全不执行。否则只执行第一步,钱就凭空消 失了,这显然让人无法接受。

事务的原理是先将属于一个事务的命令发送给Redis,然后再让Redis依次执行这些命令。例如:

redis> MULTI
OK
redis> SADD "user:1:following" 2 
QUEUED 
redis> SADD "user:2:followers" 1 
QUEUED 
redis> EXEC
1) (integer) 1 
2) (integer) 1
  1. 首先使用MULTI命令告诉Redis:“下面我发给你的 命令属于同一个事务,你先不要执行,而是把它们暂时存起来。”Redis回答:“OK。”
  2. 而后我们发送了两个 SADD命令来实现关注和被关注操作,可以看到 Redis 遵守了承诺,没有执行这些命令,而是返回QUEUED表示这两条命令已经进入等待执行的事务队列中了。
  3. 当把所有要在同一个事务中执行的命令都发给 Redis 后,我们使用 EXEC 命令告诉Redis 将等待执行的事务队列中的所有命令(即刚才所有返回QUEUED的命令)按照发送顺序依次 执行。EXEC 命令的返回值就是这些命令的返回值组成的列表,返回值顺序和命令的顺序相 同。

Redis保证一个事务中的所有命令要么都执行,要么都不执行。

  • 如果在发送EXEC命令前 客户端断线了,则 Redis 会清空事务队列,事务中的所有命令都不会执行。
  • 而一旦客户端发 送了EXEC命令,所有的命令就都会被执行,即使此后客户端断线也没关系,因为Redis中已 经记录了所有要执行的命令。

除此之外,Redis 的事务还能保证一个事务内的命令依次执行而不被其他命令插入。试 想客户端A需要执行几条命令,同时客户端B发送了一条命令,如果不使用事务,则客户端B 的命令可能会插入到客户端A的几条命令中执行。如果不希望发生这种情况,也可以使用事务。


二、错误处理

如果一个事务中的某个命令执行出错,Redis 会怎样处理呢?

1、语法错误。语法错误指命令不存在或者命令参数的个数不对

redis2.6.5之后的版本:假如跟在MULTI命令后执行了3个命令:一个是正确的命令,成功地加入事务队列;其余两个命令都有语法错误。而只要有一个命令有语法错误,执行 EXEC 命令后 Redis 就会直接返 回错误,连语法正确的命令也不会执行。

Redis 2.6.5 之前的版本会忽略有语法错误的命令,然后执行事务中其他语法正 确的命令。

2、运行错误

运行错误指在命令执行时出现的错误,比如使用散列类型的命令操作 集合类型的键,这种错误在实际执行之前 Redis 是无法发现的,所以在事务里这样的命令是会被 Redis 接受并执行的。如果事务里的一条命令出现了运行错误,事务里其他的命令依然会继续执行(包括出错命令之后的命令);

Redis的事务没有关系数据库事务提供的回滚(rollback)功能。为此开发者必须在事 务执行出错后自己收拾剩下的摊子(将数据库复原回事务执行前的状态等)。
不过由于 Redis 不支持回滚功能,也使得 Redis 在事务上可以保持简洁和快速。另外回 顾刚才提到的会导致事务执行失败的两种错误,其中语法错误完全可以在开发时找出并解 决,另外如果能够很好地规划数据库(保证键名规范等)的使用,是不会出现如命令与数据 类型不匹配这样的运行错误的。


三、watch命令

我们已经知道在一个事务中只有当所有命令都依次执行完后才能得到每个结果的返回 值,可是有些情况下需要先获得一条命令的返回值,然后再根据这个值执行下一条命令。

要实现这一思路需要请出事务家族的另一位成员:WATCH。WATCH 命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到 EXEC 命令(事务中的命令是在 EXEC 之后才执行的,所以在 MULTI 命令后可以修改 WATCH监控的键值)

redis(11):事务_第1张图片

上例中在执行 WATCH命令后、事务执行前修改了key的值(即 SET test:key 2),所以最后 事务中的命令 SET test:key 3没有执行,EXEC命令返回空结果。

学会了WATCH命令就可以通过事务自己实现incr函数了,伪代码如下:

 def incr($key) 
	 WATCH $key 
	 $value = GET $key
	  if not $value 
	  $value = 0 
	  $value = $value + 1 
	  MULTI 
	  SET $key, $value 
	  result = EXEC 
	  return result[0]

因为EXEC命令返回值是多行字符串类型,所以代码中使用result[0]来获得其中第一个结 果。

你可能感兴趣的:(#,redis,redis)