目录
Redis 事务对比 MySQL 事务
MySQL 事务
Redis 事务
Redis 事务原子性解释
Redis 事务详解
执行流程
典型使用场景
Redis 事务命令
WATCH 的使用
WATCH 实现原理
总结
阅读下文之前建议点击下方链接了解 MySQL 事务详解
MySQL 事务详解
Redis 事务对比 MySQL 事务
MySQL 事务
- 原子性:将多个操作打包成一个整体,要么全部执行成功,要么全部都不执行,一旦执行出错,立刻回滚如初
- 一致性:事务执行前后,通过约束和回滚机制,保证数据合理
- 持久性:事务做出的修改会存储到硬盘上,不会随着服务器重启而丢失
- 隔离性:事务并发执行,涉及 四大隔离级别
Redis 事务
- 原子性:Redis 的事务到底有没有原子性 存在争议
- 不具备一致性:Redis 没有约束,也没有回滚机制,事务操作过程中如果某个修改操作出现失败,就可能引起不一致的情况
- 不具备持久性:Redis 本身就是内存数据库,数据是存储在内存中的,虽然 Redis 也有持久化机制,但是这里的持久化机制 和 事务没有啥直接关系
- 不涉及隔离性:Redis 是一个单线程模型的服务器程序,所有的请求 或 事务都是串行执行的
Redis 事务原子性解释
- 原子性最原本的含义:将多个操作打包到一起,要么全部执行,要么全部不执行
- Redis 和 MySQL 均做到了原子性最原本的含义
注意:
- Redis 仅能保证 多个操作全部一起执行或者不执行,但是不保证是否能够执行成功!
- 即如果事务中若干个操作,存在有失败执行失败的操作,不会进行回滚!!
- 但是 MySQL 面对上述情况会进行回滚操作,以保证事务的 一致性!
小总结:
- MySQL 提高了原子性的门槛
- 这就使得人们谈到原子性的时候,更多的是想到的 MySQL 这种带有回滚机制的原子性
- 所以 Redis 事务是否具有原子性便存在这样的歧义
- Redis 事务有原子性:可将多个操作能够打包到一起执行
- Redis 事务不具有原子性:可将多个操作能够打包到一起执行,但是不具有回滚机制,无法保证这多个操作能够正确的执行
Redis 事务详解
- Redis 事务的主要意义就是为了打包 ,以便避免其他客户端的命令插队到中间
- Redis 引入队列来实现事务,该队列 每个客户端均有一个
执行流程
- 开启事务
- 客户端输入命令,命令发给服务器并且插入到队列中,此时的命令不会立即执行
- 输入 执行事务 命令,队列中的命令 按照顺序依次执行
- 依次执行的过程,均由 Redis 主线程完成,主线程会把事务中的操作执行完,再去执行其他客户端的命令
问题:
- Redis 事务为什么这么简单,为啥不设计成和 MySQL 事务一样强大呢?
回答:
- MySQL 事务在实现上付出了很大的代价
- 空间上:花费更多的空间来存储更多的数据
- 时间上:有着更大的执行开销
- 也正是因为 MySQL 上述的代价,才有了 Redis 上场的机会
典型使用场景
- 关于超卖问题
- 一款商品放货 5000 台,如果有 5001 个人下单成功,这便属于超卖
典型写法:
- 如果不加上任何限制,便可能存在 线程安全 问题
- 在多线程中,均通过加锁的方式,来避免 插队
Redis 事务写法:
- Redis 本身就是一个单线程模型,所以其天然不具有线程安全问题
- 所以此处我们直接使用 Redis 事务,将购买商品所需进行的流程打包到一起执行即可
注意:
- Redis 服务器只有收到 执行事务 命令的时候 才会真正执行 事务队列中的命令 !
具体理解:
- 只有客户端B 的 执行事务 命令发过来之后,服务器才会真正执行客户端B 的事务队列
- 与此同时 客户端A 的事务队列已经执行完成
- 则 客户端B 事务队列中 get 到的 count 就已经是 客户端A 事务队列执行完之后的结果
- 即采用 Redis 事务方法 无需加锁也能解决上述超卖问题
小总结:
- Redis 事务的应用场景没有 MySQL 事务那么多
- Redis 如果是按照集群模式部署的,则不支持事务
问题:
- Redis 命令中能进行条件判定吗?
回答:
- Redis 原生命令中确实没有像 if 这种的条件判定
- 但是 Redis 支持 lua 脚本
- 通过 lua 脚本便可以实现上述实例那样的条件判定,并且和事务一样,也是打包批量执行
- lua 脚本的实现方式是 Redis 事务的进阶版本
Redis 事务命令
MULTI 开启事务 exec 执行事务 discard 放弃当前事务 watch 监控某个 key 是否在事务执行之前发送变化 unwatch 放弃监控
实例理解
- 此处演示正确执行一个 Redis 事务
- 此处演示放弃执行一个 Redis 事务
问题:
- 开启事务,给服务器发送若干个命令后,此时服务器重启,那么该事务将会如何呢?
回答:
- 上述场景的效果就等同于 discard
WATCH 的使用
- watch 命令用于监控某个 key 是否在事务执行之前,发生了改变
实例理解
- 从时间上来看,客户端A 先发送 set key 222,客户端B 后发送 set key 333
- 但是由于 客户端A ,只有执行了 exec 命令,才会真正执行 set key 222 命令
- 即 exec 命令 比 客户端B 的 set key 333 命令后执行
- 所以上图中,key 的最终值为 222
注意:
- 上述场景便可使用 watch 命令来监控这个 key
- 看看这个 key 在事务 MULTI 和 exec 之间,是否被 外部其他客户端修改
WATCH 实现原理
- watch 的实现 类似于一个 乐观锁
- Redis 的 watch 命令相当于基于 版本号 这样的机制来进行实现的乐观锁
- 这样的设定 在 CAS 这里 ABA 问题中也涉及过 思想方法 还是 实现上都是非常相似的
总结
- Redis 事务,要比 MySQL 事务要简单很多
- 原子性:Redis 事务,不支持回滚
- 一致性:Redis 并不会保证事务执行前和执行后,数据统一
- 持久性:Redis 主要通过内存来存储数据
- 隔离性:Redis 自身作为一个单线程的服务器模型,上面处理的请求本质上都是串行执行的
- Redis 中的 lua 脚本,也能起到类似于事务的效果
- 官方网站上,事务这里的任何能实现的效果,都可以使用 lua 脚本代替