深入学习 Redis - 事务、实现原理、指令使用及场景

目录

一、Redis 事务 vs MySQL事务

二、Redis 事务的执行原理

2.1、执行原理

2.2、Redis 事务设计这么简单,为什么不涉及成 MySQL 那样强大呢?

三、Redis 事务的使用

3.1、使用场景

3.2、具体演示

开启/执行/放弃事务

watch 监控

watch 实现原理

小结


一、Redis 事务 vs MySQL事务


我们熟知的 MySQL,你还记得她的事务特性么?

  1. 原子性:将多个操作打包成一个整体,要么全都执行成功,要么一个都不执行,一旦执行出错,立刻回滚如初.
  2. 一致性:事务执行前后,通过约束和回滚机制,保证数据合理. 例如我去银行给 张三 转账 1 元钱,那么我的卡里就会减少 1 元,张三的卡里就会增加 1 元,不能增加 100 元。
  3. 持久性:事务做出的修改都会存储到硬盘上,不会随着服务器重启而丢失.
  4. 隔离性:事务并发执行,涉及到的一些问题.

Redis 的事务和 MySQL 相比,就像一个“弟弟”~

  1. 原子性:Redis 的事务到底有没有在原子性,存在争议!因为从 Redis 的角度理解就是“把多个操作打包到一起,要么全都执行,要么全都不执行”,也就是说,这里如果中途执行失败,那就失败吧,不会有像 MySQL 那样回滚的操作(也因此,网上有人一部分人说 redis 事务有原子性,一部分说没有原子性,都对,但是要看从那个角度出发了~).
  2. 不具备一致性:redis 没有约束和回滚,事务执行一旦出错,就可能导致不一致的情况.
  3. 不具备持久性:Redis 本身就是内存数据库,数据是存储在内存中的. 虽然 Redis 也有持久化机制,但是这里的持久化机制和事务没有什么直接关系.
  4. 不具备隔离性:Redis 是一个单线程模型的服务器程序,所有的请求 / 事务,都是 “串行” 执行的.

二、Redis 事务的执行原理


2.1、执行原理

Redis 事务的主要意义就是为了 “打包” ,避免其他客户端的命令,插队到其中,那他具体是通过什么实现的呢?Redis 实现事务,是引入了队列(每一个客户端都有),开启事务的时候,客户端输入的命令,就会发给服务器,然后进入都这个队列中,此时并不会立即执行,当收到 “执行事务” 命令的时候,就会把队列中的这些任务按照顺序依次执行(这里是由 Redis 主线程执行的,他会把事务中的操作执行完,再去执行其他客户端).

举个例子,这就像是你去做核酸检测,但是核酸检测是早上 7 点开始,但是你 6 点就来了,还发现已经有一些人在这里排成长队,所以这个时候你只能在去后面排队,不能插队,等到核酸检测开始,并且前面的人都检测完了,才轮到你~

2.2、Redis 事务设计这么简单,为什么不涉及成 MySQL 那样强大呢?

MySQL 的事务之所以能设计的这么强大(原子性、一致性....),背后是付出了很多代价的~

  1. 空间上,要花费跟多的空间来存储更多的数据,例如 MySQL 事务执行中途出错,引发的回滚机制,这种回滚机制也是需要花费空间去记录每条执行的指令是什么,才能进行回滚的.
  2. 时间上,也要有更大的执行开销,比如 MySQL 事务的持久化机制,是存储在硬盘上的,操作缓慢;再例如 MySQL 中事务遇到冲突是进行加锁,加锁也是需要开销的,涉及到用户态到内核态的转化,而且加锁就有可能产生锁竞争,一竞争就会加锁排队,一排队就不知道什么时候释放锁了~

也正因如此,Redis 才有了上场的机会。

三、Redis 事务的使用


3.1、使用场景

如果需要把多个操作打包进行,使用事务就是比较合适的~

比如说以前春运,咱们在网上抢火车票的场景、秒杀...

这里以抢火车票的场景为例:

假设现在有大量的用户都在同一时间段开始抢票,也就是说有多个 Redis 客户端执行执行抢票操作,如下:

获取仓库中剩余的票数;
if(剩余的票数 > 0) {下单成功;商品数量--;
}

这里如果不加任何限制,就有可能引发 “线程安全” 问题~  以前咱们解决线程安全问题,都是通过加锁排队来避免 “插队” 的,而 redis 这里不加锁,也能解决上述问题,直接使用事务即可,如下:

开启事务
get count
if count > 0decr count
执行事务

Ps:Redis 原生的命令中是没有上述这种判断的,但是 Redis 支持 lua 脚本,通过 lua 脚本就可以实现上述的 条件判定,并且也和事务一样是打包批量执行的.

确实,redis 的事务使用场景没有 mysql 事务那么多,并且 redis 如果是按照集群模式部署,是不支持事务的.

3.2、具体演示

redis 的事务是通过以下命令进行控制的

multi        开启事务(读作 “猫体”)
exec         执行事务
discard      放弃当前事务
watch        监控某个 key 是否在事务执行之前发生变化(必须搭配事务使用)
unwatch      放弃监控

开启/执行/放弃事务

开启事务,执行以下命令:

此时开启另一个客户端,查看这几个 key ,会发现这几个 key 并没有被赋值(说明此时还没有执行事务).,如下

如果放弃事务,就相当于什么也没有发生,如下

如果使用 exec,就会按顺序执行事务,如下

watch 监控

watch 就是用来监控某个 key 是否在事务执行之前,发生改变,但必须搭配事务来使用.

如下,用 watch 监控 key1,开启事务,并在执行事务之前,另一个客户端对 key1 进行修改,如下

执行事务后,发现 key 在外部由修改,会返回 nil ,表示事务什么都不会执行,如下

watch 实现原理

watch 的实现,类似于一个 “乐观锁”.

乐观锁,不是某个具体的锁,而是指某一类锁的特性:加锁之前,就会有一个心里预期,预期接下来锁冲突的概论比较低,就像是有些同学,考试前觉得自己平时学的很扎实,因此考试前就轻松一些,少复习一点~

redis 的 watch 就相当于基于 “版本号” 这样的机制,实现 “乐观锁”。

当执行 watch key 的时候,就会给 key 安排一个 版本号,版本号可以理解成一个“整数”,每次在修改 key 的时候,版本号都会 “变大” (这个变大是没有规律的,不是每次都增长1),然后在执行 事务 的时候,就会做出判定,判断当前这个 key 的版本号和最初 watch 的时候,记录的版本号是否一致~

  1. 如果一致,说明当前 key 在事务开启到最终执行这个过程中,没有别的客户端修改,才能真正的执行事务.
  2. 如果不一致,就说明 key 在其他客户端修改过了,因此就直接丢弃事务中的所有操作,最后返回 nil.

Ps:这样的设定,在 CAS 的 ABA 问题中也涉及到过,思想方法和实现上都是非常相似的,例如 CAS 实现自旋锁,就类似版本号设定的实现.

小结


重在学习思想,而不是理论,很多思想都是触类旁通的,当我们未来遇到某一类问题的时候,就可以回想起当时解决问题的思想方法,迎刃而解~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/59865.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Terraform学习】保护敏感变量(Terraform配置语言学习)

实验步骤 创建 EC2 IAM 角色 导航到IAM 在左侧菜单中,单击角色 。单击创建角色该按钮以创建新的 IAM 角色。 在创建角色部分,为角色选择可信实体类型: AWS 服务 使用案例:EC2 单击下一步 添加权限:现在,您可以看到…

HA3 SQL样本实验:一种混合计算查询的全新样本解决方案

作者:陆唯一(芜霜) HA3(对外开源代号:Havenask )是阿里智能引擎团队自研的大规模分布式检索系统,广泛应用于阿里内部的搜索业务,是十多年来阿里在电商领域积累下来的核心竞争力产品。Ha3 SQL 是在原有Ha3引…

Promise详细版

promise基础原理到难点分析 常见的Promise的方法解读 扩展async和await深入分析 逐步分析Promise底层逻辑代码 一、Promise基础 1.什么是promise 为了解决回调地狱: //2.设置点击事件btn.onclick function() {//3.创建ajax实例化对象let xhr new XMLHttpRe…

C++/Qt读写ini文件

今天介绍C/Qt读写ini文件,ini文件一般是作为配置文件来使用,比如一些程序的一些默认参数会写在一个ini文件中,程序运行时会进行对应的参数读取,详细可以查看百度ini文件的介绍。https://baike.baidu.com/item/ini%E6%96%87%E4%BB%…

Spring Cloud Gateway

一 什么是Spring Cloud Gateway 网关作为流量的入口,常用的功能包括路由转发,权限校验,限流等。 Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul。相比 Zuul 来说,Spring Clo…

【导出Word】如何使用Java+Freemarker模板引擎,根据XML模板文件生成Word文档(只含文本内容的模板)

这篇文章,主要介绍如何使用JavaFreemarker模板引擎,根据XML模板文件生成Word文档。 目录 一、导出Word文档 1.1、基础知识 1.2、制作模板文件 1.3、代码实现 (1)引入依赖 (2)创建Freemarker工具类 &…

W5500-EVB-PICO作为TCP Client 进行数据回环测试(五)

前言 上一章我们用W5500-EVB-PICO开发板通过DNS解析www.baidu.com(百度域名)成功得到其IP地址,那么本章我们将用我们的开发板作为客户端去连接服务器,并做数据回环测试:收到服务器发送的数据,并回传给服务器…

【分布式系统】聊聊流量和数据调度

对于分布式系统来说,除了监控层面、以及服务治理层面,还有两个层面流量和数据调度。流量调度其实比较好理解,就是用户请求的流量,如何按照一定的策略算法打到不同的机器上。以及如何实现一个高可用、高性能的流量调度平台。而数据…

BM5 合并k个已排序的链表 javascript

描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围: 示例1 输入: [{1,2,3},{4,5,6,7}] 返回值: {1,2,3,4,5,6,7}示例2 输入: [{1,2},{1,4,5},{6}] 返回值: {1,1,2,4,5,6}解题思路 利用两个…

Electron学习1 安装环境与第一个程序

Electron学习1 安装环境与第一个程序 一、 Electron 简介二、安装 nvm三、安装nodejs四、安装nrm五、安装electron1. npm 初始化2. 创建 package.json3. 安装electron4. 创建一个页面5. 创建文件main.js6. 创建预加载器文件 preload.js7. 启动程序 六、打包 一、 Electron 简介…

Qt多线程编程

本章介绍Qt多线程编程。 1.方法 Qt多线程编程通常有2种方法: 1)通过继承QThread类,实现run()方法。 2)采用QObject::moveToThread()方法。 方法2是Qt官方推荐的方法,本文介绍第2种。 2.步骤 1)创建Worker类 这里的Worker类就是我们需要…

Nginx安装以及LVS-DR集群搭建

Nginx安装 1.环境准备 yum insatall -y make gcc gcc-c pcre-devel #pcre-devel -- pcre库 #安装openssl-devel yum install -y openssl-devel 2.tar安装包 3.解压软件包并创建软连接 tar -xf nginx-1.22.0.tar.gz -C /usr/local/ ln -s /usr/local/nginx-1.22.0/ /usr/local…