redis基础四(事务)

1、事务是什么

可以一次执行多个命令,本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其他命令插入,不许加塞。

2、能干啥

   一个队列中,一次性、顺序性、排他性的执行一系列命令。

3、怎么玩

3.1 常用命令

Multi:标记一个事务的开始;

Exec:执行事务块内的所有命令;

Discard :取消事务,放弃执行事务块内的所有命令;

Unwatch:取消watch命令对所有key的监听;

Watch key [key…]:监听一个或多个key,如果在事务执行之前这些key被其他命令打断,那么事务将被打断。

Case1:正常执行:

 

 

在执行完 multi命令后,开启事务,之后的所有命令都是放入到队列中的,执行exec命令时提交事务,此时执行队列中的各个命令,并把结果返回。

Case2 :放弃事务:

 

也就是在执行multi命令开启事务后,执行一部分操作后,放弃该事务,执行discard命令即可。我们也可以看到后面在执行exec提交事务是会报异常的。这种情况怎么办呢?

 

Watch key后,对key的value进行了修改,只能unwatch,然后在watch。想想我们通过version控制数据库的乐观锁也是一样。

Case3:事务的原子性(要么都成功,要么都失败)部分支持:

1)、执行命令报错,未加入队列,全体回滚:

开启事务后,先执行一条插入语句,set  k1 v1 没问题,紧跟着执行set k2 不符合redis的语法直接报错,后面在执行exec提交事务操作时直接报错,导致整个事务块的内容都没提交; 原因就是set k2 这条命令未加入队列中报错,导致事务全部回滚。

2)、执行命令不报错,已经加入队列,

但是redis内部执行报错,此时只有那一条命令不成功,其余均成功

看下上面的命令,也是开启一个事务,首先是set k1 v1 成功执行,紧跟着执行incr k1 把v1的值加1,我们知道k1对应的值为字符v1 ,不能加1,但是这个命令incr k1还是成功的放入队列中了,之后我们又把set k2 v2,执行提交事务操作。我们看到k1 和k2 都已经成功插入到redis中,唯有incr k1 没成功执行,这就是redis的事务的部分支持。

Case4:watch监控

悲观锁:每次去拿数据时会认为别人会去修改数据,所以每次去拿数据的时候都会上锁,这样别人拿数据就会block直到到释放锁,传统的数据库就用到很多这样的锁,比如行锁,表锁,读锁,写锁都是在操作前先上锁。

乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

 

Watch指令类似与乐观锁,事务提交时,如果某个key的值已经被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行。

 通过watch命令在事务执行之前监控多个keys,倘若在watch之后有任务key的值发生了变化,exec命令执行的事务都被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

同一线程操作且未改变值。

 

在同一个线程内,首先对balance加锁(这时候其他线程对balance也可以进行任何其他操作),然后开启一个事务,对balance减20,debt加20操作,最后提交事务,可以看到正确执行。

同一线程操作但值已经发生改变

在同一个线程中,watch balance的值后,又对其进行了修改,这时候开启事务并对balance和debt的值进行修改,我们可以发现提交事务还是报错,这是因为watch balance时记录的balance对应的值是500;后面把它的值改为200;而在提交事务时是把redis中的balance对应的当前值与watch时的值比较的,不一样就不运行提交事务。

A线程加watch key,B线程修改key的值

A线程对balance加watch,然后开启事务,对balance和debt的值分别进行了减、增20,此时还没提交事务,线程B这时候对balance进行了更改操作,那么我们在线程a内提交事务会报错。

   这和db2数据库通过version字段对每行数据加锁机制是类似的:在操作前先查询下version的值,提交事务前在查询一次,两者一致这提交事务否则报错。这里也是一样,先监控balance的值,提交事务前在查询下与watch时的值是否一致,不一致就不能提交事务。

6.4  阶段

开启:以multi开始一个事务;

入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面;

执行:由exec命令触发事务。

 

6.5  特性

单独的隔离操作:事务中的所有命令都会序列化,按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断;

没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务的提交前任何指令都不会被实际执行; 也就不存在“事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头疼的问题;

不保证原子性:redis同一个事务中如果有一条命令执行失败,如是redis内部的失败,命令本身入队,则其他命令都会成功执行,若命令本身执行就报错,压根就没有如队列,那么事务内的全部命令都回滚。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值