一、事务
redis 对事务的支持目前还比较简单。 redis 只能保证一个 client 发起的事务中的命令可以连
续的执行,而中间不会插入其他 client 的命令。
由于 redis 是单线程来处理所有 client 的请求的所以做到这点是很容易的。一般情况下 redis 在接受到一个 client 发来的命令后会立即处理并 返回处理结果,但是当一个 client 在一个连接中发出 multi 命令,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此连接
受到 exec 命令后,redis 会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给 client.然后此连接就 结束事务上下文。
下面看一个例子:
从这个例子我们可以看到 2 个 set age 命令发出后并没执行而是被放到了队列中。调用 exec
后 2 个命令才被连续的执行,最后返回的是两条命令执行后的结果。
如何取消一个事务?
我们可以调用 discard 命令来取消一个事务,让事务回滚。接着上面例子
可以发现这次 2 个 set age 命令都没被执行。discard 命令其实就是清空事务的命令队列并退
出事务上下文,也就是我们常说的事务回滚。
二、乐观锁
乐观锁:大多数是基于数据版本(version)的记录机制实现的。何谓数据版本?即为数据增
加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表添加一个
“version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加 1。
此时,将提交数据的版本号与数据库表对应记录的当前版本号进行比对,
如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
操作员 A |
操作员 B |
(1)、操作员 A 此时将用户信息读出(此时 version=1) 并准备从其帐户余额中扣除$50,($100-$50) |
(2)、在操作员 A 操作的过程中,操作员 B 也读入此用户信息(此时 version=1),并准备从其帐户余额中扣除$20($100-$20) |
(3)、操作员 A 完成了修改工作,将数据版本号加 1(此时 version=2),连同帐户扣除后余额(balance=$50),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version更新为 2 |
|
(4) 、 操 作 员 B 完 成 了 操 作 , 也 将 版 本 号 加 1 ( version=2 ) 并 试 图 向 数 据 库 提 交 数 据 (balance=$80),但此时比对数据库记录版本时发 现,操作员 B 提交的数据版本号为 2,数据库记录 当前版本也为 2,不满足“提交版本必须大于记录 当前版本才能执行更新”的乐观锁策略,因此,操 作员 B 的提交被驳回 |
这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果来覆盖操作员 A 的操作结果
的可能。
即然乐观锁比悲观锁要好很多,redis 是否也支持呢?答案是支持, redis 从 2.1.0 开始就支持
乐观锁了,可以显式的使用 watch 对某个 key 进行加锁,避免悲观锁带来的一系列问题。
Redis 乐观锁实例:假设有一个 age 的 key,我们开 2 个 session 来对 age 进行赋值操作,我
们来看一下结果如何。
session1 |
session2 |
第一步: 127.0.0.1:6379> get age |
|
第二步: 127.0.0.1:6379> set age 30 |
|
第三步: 127.0.0.1:6379> set age 10 |
从以上实例可以看到在
第一步,Session 1 还没有来得及对 age 的值进行修改
第二步,Session 2 已经将 age 的值设为 30
第三步,Session 1 希望将 age 的值设为 20,但结果一执行返回是 nil,说明执行失败,之后
我们再取一下 age 的值是 30,这是由于 Session 1 中对 age 加了乐观锁导致的。
watch 命令会监视给定的 key,当 exec 时候如果监视的 key 从调用 watch 后发生过变化,则整
个事务会失败。也可以调用 watch 多次监视多个 key.这 样就可以对指定的 key 加乐观锁了。
注意 watch 的 key 是对整个连接有效的,事务也一样。如果连接断开,监视和事务都会被自
动清除。当然了 exec,discard,unwatch 命令都会清除连接中的所有监视。
redis 的事务实现是如此简单,当然会存在一些问题。第一个问题是 redis 只能保证事务的每
个命令连续执行,但是如果事务中的一个命令失败了,并不回滚其他命令,比如使用的命令
类型不匹配。下面将以一个实例的例子来说明这个问题:
从这个例子中可以看到,age 由于是个数字,那么它可以有自增运算,但是 name 是个字符
串,无法对其进行自增运算,所以会报错,如果按传统关系型数据库的思路来讲,整个事务
都会回滚,但是我们看到 redis 却是将可以执行的命令提交了,所以这个现象对于习惯于关
系型数据库操作的朋友来说是很别扭的,这一点也是 redis 今天需要改进的地方。
本文转自shayang8851CTO博客,原文链接:http://blog.51cto.com/janephp/1339572,如需转载请自行联系原作者