文章目录
- 一.认识Redis的事务
- 二.Redis事务的意义
- 三.Redis事务的应用场景
- 四.事务的相关命令
一.认识Redis的事务
MySQL的事务:
- 原子性: 将多个操作,打包成一个整体
- 一致性:事务执行前后,数据是符合预期的;
- 持久性:事务的修改都要进行落盘
- 隔离性:处理并发问题;
1.Redis既可以说有原子性,也可以说没有原子性(推荐);
- 原子性最初的含义是:将多个操作进行打包,要么全部执行,要么都不执行
- 这也是Redis所谓的"原子性";
- 但是,MySQL中的原子性还保证了这些操作要么全部执行成功,要么都不执行;
- 即MySQL提高了原子性的门槛,使得别人谈到原子性,都会想到的是这种带有回滚的原子性;
2.Redis没有一致性: 其没有约束,没有回滚机制,事务执行时如果出现修改操作失败,则可能产生不一致的问题;
3.Redis没有持久性:因为Redis是内存数据库,数据存储在内存中;虽然其有持久化机制,但这和事务是没有关系的;
4.Redis没有隔离性:Redis是一个单线程模型的服务器程序,所有的请求/事务,都是"串行"执行的;
二.Redis事务的意义
Redis事务存在的意义:
- 为了进行"打包",避免其他客户端命令,进行插队;
这里的意思是:
有些操作是一起的,但服务器并不一定能一起处理他们,可能被其他客户端进行插队,因此,引入事务,让这些操作"打包",每次一起进行处理;
Redis在每个客户端都引入了一个队列,开启事务时,客户端输入的命令,都会发送给服务器并进入该队列中(不是立即执行);
该队列是在服务器上的,每个客户端和服务器的连接都有这样的队列。客户端发的请求会缓存在该队列中; 可以视作客户端和服务器建立了TCP连接,通过连接管理管理这个队列
当遇到了“执行事务”命令时,此时就会把队列中的任务都按照顺序依次执行;(由主线程完成,主线程完成事务的操作后,再处理其他客户端)
问题一:Redis事务为什么设计的如此简单,为什么不像MySQL一样?
- MySQL的事务,在背后付出了很多的代价;
– 空间上,要花费更多的空间存储更多的数据(索引,日志,。。。。。。)
– 时间上,存在更大的执行开销(持久化,锁引起的阻塞等)
三.Redis事务的应用场景
假设现在有一个付费场景,限量5000人,之外的人无法下单,如何设计?(秒杀)
//经典写法intn=剩余商品的个数if(n>0){下单;剩余商品--;}如上的写法显然存在线程安全问题;
使用事务可以很好的解决如上问题;
开启事务后,每个客户端的这些操作发给服务器都不会立即执行,而是先让入队列中,当收到执行事务的命令后,才执行这些命令且是一次性完成后才会去处理其他请求;
=》如上,未通过加锁,就解决了超卖问题;
Redis的事务实际的应用场景并不如MySQL那样多;(甚至有点鸡肋);
问题一:虽然维护了队列,但是服务器怎么知道这些命令是我这个客户端的呢?
- Redis和客户端建立了诸如TCP连接,通过连接管理就可以知道哪些数据属于谁;
四.事务的相关命令
MySQL事务相关指令主要如下:
- 开启事务: multi
- 执行事务: exec
- 放弃当前事务: discard
watch命令:监控某个key看其是否在执行前(multi和exec之间)发生了改变;
watch的实现原理:
- watch的实现类似于乐观锁或者悲观锁(不是真锁,只是锁的特性);
- Redis的watch相当于基于版本号这样的机制实现了乐观锁;
- 当事务执行exec时,会对版本号进行判定,判断当前的版本号和最初watch的版本号是否一致:
– 如果一致,说明key在事务开启到执行过程中,没有其他客户端对该key进行修改,执行;
– 如果不一致,说明key在该事务期间被其他客户端进行了修改,则放弃事务中的操作.exec返回nil;- 类似于CAS的ABA问题;
Redis中支持lua脚本,可以起到类似于事务的效果,事务的任何效果,都可以用lua脚本替代;