Redis Watch命令
client1
127.0.0.1:6379> watch lc
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set lc 5
QUEUED
127.0.0.1:6379> exec
1) OK
127.0.0.1:6379> get lc
"5"
127.0.0.1:6379> watch lc
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set lc 99
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get lc
"111"
client2
127.0.0.1:6379> get lc
"5"
127.0.0.1:6379> set lc 111
OK
在client1执行watch lc 这个key,在multi中对lc进行写操作
在exec之前,在client2中,对lc修改:set lc 111
watch监听到lc的变化了, set lc 99
命令,在 exec的时候,执行不成功,返回了nil
最后lc的结果是111
client1
127.0.0.1:6379> watch lc
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set lc 114
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get lc
"115"
client2
127.0.0.1:6379> watch lc
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set lc 115
QUEUED
127.0.0.1:6379> exec
1) OK
假设 client2 先于client 对lc进行了写操作: set lc 115
client1 的exec是执行失败的,返回nil。 这样,最终的lc值为115
当有两个事务同时并发对一个资源进行写操作,先进行操作且事务提交的任务会成功,后进行事务提交的任务通过watch机制,对该资源执行会失败。实际测试,是在multi -> exec 之间代码块执行的命令都会失败,也就是事务中所有执行命令都一起失败。
watch和乐观锁
可以使用watch + multi + exec, 实现 乐观锁
(简单的多版本控制)
使用乐观锁
的机制,可以利用到FIFO(先进先出)的优先队列中。比如抢购,先抢到的人成功,后抢的人直接失败。如果不希望有失败发送,可以将失败的任务,又重新放回优先队列中
使用悲观锁
的缺点很明显,会导致较长时间的阻塞等待。但会一定程度保证所有任务都执行,没有失败情况
如果要追求并发性能,可以接收失败情况,乐观锁
的方式是更好的选择
如果对失败没有那么严格的要求,可以乐观锁
+失败重试
的方式
如果严格要求顺序,并且尽可能减少失败,用悲观锁
的方式(给这块资源加锁)
关于队列方面,又有几种加锁方式:
- 在入队的时候加锁(生产者时)
- 在出队列的时候加锁(消费者时)
- 在入队列和出队列的时候都加锁
需要根据具体的需求情况进行。大部分业务,应该是在出队列的时候加锁(消费者时)
,不用关心入队顺序情况,会是更好的选择
当然,选择用了Redis来实现,出现性能瓶颈很可能就转移到了Redis上