Redis 事务

原创
2016/09/10 09:48
阅读数 137

 

先手动执行如下命令,然后看看效果

>multi

>set str1 "hello world"

>"QUEUED"

>set str2 hello world

>"QUEUED"

>set str2 "welcome"

>"QUEUED"

>exec

> 1) "OK"

> 2) "ERR syntax error"

> 3) "OK"

>get str1

>"hello world"

>get str2

>null

>get str3

>welcome

 

1)事务以 multi 开始 exec 结束,在exec执行之前的命令,都会存储在Redis服务端的命令队列里面直到exec调用才会执行,因此,任何multi与exec之间的命令都不会及时反馈结果。因此我们无法在事务里面通过读取Redis数据来做决定,这就说明你只能是在multi之前把需要从Redis获取的数据读取出来,然后在到事务里面去做决定,哈哈,这是不是太蠢了,因为这是脏读。

2)从示例结果你可以看出,事务在set str2 发生语法错误,可是整个结果却没有保证数据的原子性(要么成功,要么失败),因为 str1插入成功,str3插入成功,str2插入失败。这说明Redis服务端执行队列里的命令时遇到错误不会回滚也不会停止运行,Redis是这样设计的,理由是回滚是多余的操作,因为错误都是程序员代码造成的,换句话时候代码中不应该出现错误,也就没必要回滚了。总之, 即使事务中有某条/某些命令执行失败了, 事务队列中的其他命令仍然会继续执行 —— Redis 不会停止执行事务中的命令。

3)那么如何解决数据脏读呢?MySQL使用的事务锁机制,很明显高并发下这是不可取的手段,看看Redis是如何处理的,使用 watch(key1,key2...) 命令,在multi之前执行 watch命令,观察监视一个或多个key,如果在接收到exe命令时发现被监视的任何一个key的值有了变化,那么事务就被取消,这里是有原子性的哦,另外,watch 提供的类似CAS(Compare And Set or Check And Set)功能,另watch 其实是乐观锁机制。

4)watch之后,multi之前,这段代码当中可能就会出现值发生变化的时候,那么我们可以在这之间再来做一个判断,判断值是否满足我们预期,如果不满足预期那么就直接 unwatch(exec、discard之后默认执行了unwatch). 然后不执行事务。这其实就是双重锁定,第一层锁放在watch之前,第二层锁放在watch之后multi之前,是不是很熟悉的操作,没错,经典的懒汉式单例模式。

5)没错,unwatch 是希望在执行事务之前就发现数据已经不满足预期,然后手动控制不要去执行事务,那么discard可以在multi至exec之间执行,把事物取消,同样他取消的是整个事务。

6)使用事务的过程中,事务包裹的命令是不会被其他客户端所打扰,另外,底层客户端也会使用流水线来进一步提高事务执行时的性能,换句话说使用事物就默认使用了流水线操作,但是事务的缺点是资源耗费,还可能导致其他重要命令被延迟执行。

7)来看看完整的事务

>set str1 10

>set str2 100

>watch str1

>//伪代码,如果str2的值没有达到101则取消观察,停止程序. if( str2 <= 100 ) unwatch; return;

>multi

>incr str1

>"QUEUED"

>// 伪代码,这里可以在满足某个条件的情况下取消事务的所有命令,discard

>incr str2

>"QUEUED"

>exec

> 1) 11

> 2) 102

 

 

 

 

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部