目录
一、Redis与 mysql事务的对比
描述 | Mysql | Redis |
开启 | start transaction | multi |
语句 | 普通sql | 普通命令 |
失败 | rollback 回滚 | discard 放弃执行事务 |
成功 | commit | exec |
开两个终端来查看结果:
终端一:
127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> incr a #已经进入队列,在队列里面看不到执行结果
QUEUED
127.0.0.1:6379> get a #在队列里面看不到执行结果
QUEUED
127.0.0.1:6379> exec # 执行,执行之后看到结果
1) (integer) 2
2) "2"
127.0.0.1:6379>
终端二:
127.0.0.1:6379> get a
"1"
127.0.0.1:6379> get a
"1"
127.0.0.1:6379> get a #直到exec时,事务才起作用
"2"
取消事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set b 2
QUEUED
127.0.0.1:6379> discard #取消事务,命令DISCARD必须在MULTI中的事事务块中执行,否则会报错: ERR DISCARD without MULTI
OK
127.0.0.1:6379> exec #使用discard后,事务就释放了,使用exec会报错
(error) ERR EXEC without MULTI
二、思考redis中的锁
问题:我正在买票,而票只有1张, 如果在我multi之后,和exec之前, 票被别人买了---即ticket变成0,我该如何观察这种情景,并不再提交?
悲观的想法:世界充满危险,肯定有人和我抢, 给 ticket上锁, 只有我能操作. [悲观锁]
乐观的想法:没有那么多人和我抢,因此,我只需要注意,--有没有人更改ticket的值就可以了 [乐观锁]
Redis的事务中,启用的是乐观锁,只负责监测key没有被改动.
注意:
在1个终端中启用事务,如果事务没有结束前(没有使用exec),另一个终端可以对值进行修改,但是改了无效,还是按照事务中的值走,这个时候就违背了事务的特性,可以采用乐观锁
所以,这个时候可以启用watch对该值进行监控,如果发现值被修改,则事务取消。
watch key… | 监视一个或多个key,如果在事务执行之前这些被监视的key被其他命令修改,这该事务则被取消 |
unwatch | 取消watch命令对所有key的监视 |
例子:
在终端一中:
127.0.0.1:6379> watch a #监控a的值
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr a
QUEUED
127.0.0.1:6379> exec
(nil)
在终端二中改变a的值:
127.0.0.1:6379> set a 1008
OK
127.0.0.1:6379> get a
"1008"
备注:
事务提交时,如果Key的值已被别的客户端改变,比如某个值已被别的客户端(线程)修改过了,整个事务队列都执行失败,整个事务全部回滚。
三、Redis中的事务有四种表现形式
1、正常执行 。整个事务执行正常,全部命令都正常执行。
2、放弃事务。当用户觉得此次事务不必要执行时通过DISCARD放弃本次事务的执行,这时事务块中的所有命令全部取消执行。
3、全体执行成功或失败。类似于关系型数据库中的原子操作。只要一个命令执行失败,则全部失败。注:因为redis是部分事务,所有只有当报出严重的错误时redis才认为是必须全部失败。
4、部分执行成功。这里才真正体现出redis的部分事务这个方面,当提交事务前redis并未报出语句的相关错误,则如果执行中发现错误那么有错误的会取消执行,而其他没有错误的则会正常执行成功。redis中只有在执行事务前就出错的情况才会一直取消执行。