事务最大的一个特征就是原子性,保证了对数据的原子性操作。
Mysql 中实现事务一般步骤是:启动事务,执行 select …… for update,对要操作的数据行上锁,也就是行级锁。此时,如果并发的进程处理到处于锁定状态的数据行时,首先判定该行是否被其他进程上读锁,如果是,则一定先等待锁释放,再执行操作,这种对任何操作“都不信任”的锁,即是悲观锁。
在 redis 中实现事务有多种方法,此处讲解用 watch 监控某个 key 是否被其他进程修改,watch 乐观锁的实现方案,也就是说,先假定所有的而进程之间的数据操作没有任何交集,执行一系列的操作,在最后提交的时候,如果发现被监控的key 已经被其他的进程修改了,则提交时返回 false。
PHP 中使用 redis 的watch 实现示例如下:
$redis = new \Redis(); $redis->connect('127.0.0.1', 6379); //将被监控的key $watchKey = 'watchkey'; //监控 key, 必须在事务启用之前执行监控, //因为在执行 multi 命令之后,所有的命令将会进入到一个 redis 队列,然后保证这些命令可以不被其他程序打扰的情况下依次执行, //这也就是redis的事务原子性操作的方法 $redis->watch($watchKey); //启用事务 $redis->multi(); //睡眠提供同时操作机会 sleep(3); //修改被监控的键值,incr 是redis 的计数器,原子性操作,执行一次增加1 //这里计数增加两次 $redis->incr($watchKey); $redis->incr($watchKey); //在事务内的所有操作都将返回 redis 连接对象,只有在执行 exec 命令后返回结果中,所有的返回数据将反倒数组中返回 $redis->get($watchKey); //执行事务,如果事务执行失败则返回false, 正确则返回事务中的所有返回结果 $result = $redis->exec(); var_dump($result);
执行结果如下图:
可以发现,只要第一个事务执行成功,而后面两个都是失败的。
理论上,如果三次都执行成功,那么watchkey的值的增加量应该是 6,redis-cli 中我们查看 watchkey 的值的变化:
结果发现,watchkey 的值也仅仅只增加了 2,再一次证实,上述三次触发执行中,只有第一次事务是执行成功的。