## 1. 事务基础知识
### 1.1 存储引擎支持情况
SHOW ENGINES 命令来查看当前 MySQL 支持的存储引擎都有哪些,以及这些存储引擎是否支持事务。目前只有 InnoDB 是支持事务的。
### 1.2 基本概念
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
事务处理的原则:保证所有事务都作为 一个工作单元 来执行,即使出现了故障,都不能改变这种执行方 式。当在一个事务中执行多个操作时,要么所有的事务都被提交( commit ),那么这些修改就 永久 地保 存下来;要么数据库管理系统将 放弃 所作的所有 修改 ,整个事务回滚( rollback )到最初状态。
### 1.3 事务的ACID特性
* 原子性(atomicity):原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。
* 一致性(consistency):一致性是指事务执行前后,数据从一个 合法性状态 变换到另外一个 合法性状态 。这种状态 是 语义上 的而不是语法上的,跟具体的业务有关。
* 隔离型(isolation):一个事务的执行 不能被其他事务干扰 ,即一个事务内部的操作及使用的数据对 并发 的 其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
* 持久性(durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是 永久性的 ,接下来的其他操作和数据库 故障不应该对其有任何影响。
持久性是通过 事务日志 来保证的。日志包括了 重做日志 和 回滚日志 。当我们通过事务对数据进行修改 的时候,首先会将数据库的变化信息记录到重做日志中,然后再对数据库中对应的行进行修改。这样做 的好处是,即使数据库系统崩溃,数据库重启后也能找到没有更新到数据库系统中的重做日志,重新执 行,从而使事务具有持久性。
### 1.4 事务的状态
MySQL根据这些操作所执 行的不同阶段把 事务 大致划分成几个状态:
* 活动的(active) 事务对应的数据库操作正在执行过程中时,我们就说该事务处在 活动的 状态。
* 部分提交的(partially committed) 当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并 没有刷新到磁盘 时,我们就说该事务处在 部分提交的 状态。
* 失败的(failed) 当事务处在 活动的 或者 部分提交的 状态时,可能遇到了某些错误(数据库自身的错误、操作系统 错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在 失败的 状态。
* 中止的(aborted)如果事务执行了一部分而变为 失败的 状态,那么就需要把已经修改的事务中的操作还原到事务执 行前的状态。换句话说,就是要撤销失败事务对当前数据库造成的影响。我们把这个撤销的过程称 之为 回滚 。当 回滚 操作执行完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该事 务处在了 中止的 状态。
* 提交的(committed) 当一个处在 部分提交的 状态的事务将修改过的数据都 同步到磁盘 上之后,我们就可以说该事务处 在了 提交的 状态。
![](https://img.kancloud.cn/22/d0/22d0d1843bdc434d4d0f74fa5edb84b3_1447x935.png)
## 2. 如何使用事务
### 2.1 显式事务
START TRANSACTION 或者 BEGIN ,作用是显式开启一个事务。
```
mysql> BEGIN;
#或者
mysql> START TRANSACTION;
```
## 3. 事务隔离级别
### 数据并发问题
1. 脏写( Dirty Write ) 事务A修改了事务B未提交的的数据。
2. 脏读( Dirty Read ) 事务A读取了事务B修改但未提交的的数据。
3. 不可重复读( Non-Repeatable Read ) 事务A读取了一个字段,事务B修改了此字段,当事务A再次读取此字段时,值不同了,那就意味着发生了不可重复读。
4. 幻读( Phantom ) 事务A读取了一个字段,然后事务B在该表中插入了一些新的行,之后事务A再次读取同一个表,就会多出几行。
### 四种隔离级别
* READ UNCOMMITTED :读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结 果。不能避免脏读、不可重复读、幻读。
* READ COMMITTED :读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做 的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可 重复读、幻读问题仍然存在。
* REPEATABLE READ :可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提 交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍 然存在。这是MySQL的默认隔离级别。
* SERIALIZABLE :可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止 其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避 免脏读、不可重复读和幻读。
![](https://img.kancloud.cn/16/21/1621661177f80ec0159b53ae4079f258_1649x556.png)
![](https://img.kancloud.cn/d1/a3/d1a36d683449ee34ef2eb9abf76fbeb6_1545x1047.png)
MySQL支持的四种隔离级别
MySQL的默认隔离级别为 REPEATABLE READ **可重复读** (Oracle,SqlServer中都是选择读已提交(Read Commited))
```
# 查看隔离级别,MySQL 5.7.20的版本之前:
SHOW VARIABLES LIKE 'tx_isolation';
# 查看隔离级别,MySQL 5.7.20的版本及之后:
SHOW VARIABLES LIKE 'transaction_isolation';
# 或者不同MySQL版本中都可以使用的:
SELECT @@transaction_isolation;
```
**如何设置事务的隔离级别**
修改事务的隔离级别:
```
# 使用 GLOBAL 关键字(在全局范围影响):只对执行完该语句之后产生的会话起作用
# 使用 SESSION 关键字(在会话范围影响):
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;
#其中,隔离级别格式:
> READ UNCOMMITTED
> READ COMMITTED
> REPEATABLE READ
> SERIALIZABLE
或者
SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = '隔离级别'
#其中,隔离级别格式:
> READ-UNCOMMITTED
> READ-COMMITTED
> REPEATABLE-READ
> SERIALIZABLE
```
数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离**级别越高,数据一致性
就越好,但并发性越弱。**
## 4. 事务的常见分类
从事务理论的角度来看,可以把事务分为以下几种类型:
* 扁平事务(Flat Transactions)
* 带有保存点的扁平事务(Flat Transactions with Savepoints)
* 链事务(Chained Transactions)
* 嵌套事务(Nested Transactions)
* 分布式事务(Distributed Transactions)
## 5. MySQL事务日志
事务有4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢?
* 事务的**隔离性**由** 锁机制** 实现。
* 而事务的原子性、一致性和持久性由事务的 redo 日志和 undo 日志来保证。
* REDO LOG 称为 **重做日志** ,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的**持久性**。
* UNDO LOG 称为 **回滚日志** ,回滚行记录到某个特定版本,用来保证事务的**原子性、一致性**。
### 5.1 redo日志 重做日志
#### 为什么需要 redo 日志?
已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系 统崩溃,在重启后也能把这种修改恢复出来。没有必要在每次事务提交时就把该事务在内 存中修改过的全部页面刷新到磁盘,只需要把 修改 了哪些东西 记录一下 就好。
![](https://img.kancloud.cn/fb/f7/fbf7bebd63fad3e9202910c3e0960c3d_1523x715.png)
REDO日志的好处、特点
* 好处
* redo日志降低了刷盘频率 (保存到硬盘的频率)
* redo日志占用的空间非常小
* 特点
* redo日志是顺序写入磁盘的
* 事务执行过程中,redo log不断记录
redo的组成
* 重做日志的缓冲 (redo log buffer) ,保存在内存中,是易失的。
* 重做日志文件 (redo log file) ,保存在硬盘中,是持久的。
redo的整体流程
![](https://img.kancloud.cn/6c/b5/6cb5bdda40f5334c12eb023ab746579c_1144x378.png)
1. 先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
2. 生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
3. 当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加 写的方式
4. 定期将内存中修改的数据刷新到磁盘中
redo log的刷盘策略
**先保存在缓存池,然后再写入磁盘**
redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以 一 定的频率 刷入到真正的redo log file 中。
![](https://img.kancloud.cn/9b/24/9b24b1569a796ad53ceb8ae32b57934b_1126x597.png)
**注意**,redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到 **文件系统缓存** (page cache)中去(这是现代操作系统为了提高文件写入效率做的一个优化),真正的写入会交给系统自己来决定(比如page cache足够大了)。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那么数据也丢失了
InnoDB给出 innodb_flush_log_at_trx_commit 参数该参数控制 commit提交事务 时,如何将 redo log buffer 中的日志刷新到 redo log file 中。它支持三种策略:
* **设置为 0** :表示**每次**事务提交时**不进行刷盘**操作。(系统默认master thread每隔1s进行一次重做日 志的同步)
* **设置为 1** :表示**每次**事务提交时都将进行**同步**,**刷盘**操作( 默认值 )
* **设置为 2** :表示**每次**事务提交时都只把 **缓存池**(redo log buffer) 内容写入 **文件系统缓存**(page cache),**不进行同步**。由操作**系统自己决定**什么时候同步到磁盘文件。
### 5.2 Undo日志 回滚日志
redo log是事务持久性的保证,undo log是事务原子性的保证。在事务中 更新数据 的 前置操作 其实是要
先写入一个 undo log 。
如何理解Undo日志
事务需要保证 原子性 ,也就是事务中的操作要么全部完成,要么什么也不做。但有时候事务执行到一半
会出现一些情况,如宕机,手动需要回滚数据,这需要把数据回滚到初始状态
Undo日志的作用
**1. 回滚数据
2. MVCC**
**undo的存储结构**
1. 回滚段与undo页, InnoDB对undo log的管理采用段的方式,也就是 回滚段(rollback segment) 。每个回滚段记录了 1024 个 片段(undo log segment) ,而在每个undo log segment段中进行 undo页 的申请。
* InnoDB1.1版本之前 只有一个rollback segment,但是在1.1 及其后面,支持最大 128个rollback segment,故其支持同时在线的事务限制提高到 了 128\*1024
2. 回滚段与事务
* 每个事务只会使用一个回滚段,一个回滚段在同一时刻可能会服务于多个事务。
* 当一个事务开始的时候,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数 据会被复制到回滚段。
* 在回滚段中,事务会不断填充盘区,直到事务结束或所有的空间被用完。如果当前的盘区不够 用,事务会在段中请求扩展下一个盘区,如果所有已分配的盘区都被用完,事务会覆盖最初的盘 区或者在回滚段允许的情况下扩展新的盘区来使用。
* 回滚段存在于undo表空间中,在数据库中可以存在多个undo表空间,但同一时刻只能使用一个 undo表空间。
* 当事务提交时,InnoDB存储引擎会做以下两件事情:
* 将undo log放入列表中,以供之后的purge操作
* 判断undo log所在的页是否可以重用,若可以分配给下个事务使用
3. 回滚段中的数据分类
* 未提交的回滚数据(uncommitted undo information)
* 已经提交但未过期的回滚数据(committed undo information)
* 事务已经提交并过期的数据(expired undo information)
undo的类型
在InnoDB存储引擎中,undo log分为:
* insert undo log
* update undo log
- 学习地址
- MySQL
- 查询优化
- SQL优化
- 关于or、in、not in、!=等走不走索引的说明
- 千万级数据查询优化
- MySQL 深度分页问题
- 嵌套循环 Block Nested Loop 导致索引查询慢
- MySQL增加日志统计表优化各种日志表的统计功能
- MySQL单机读写QPS(性能)优化
- sqlMode 置 select 的值可以比 group 里的多
- drop、delete、truncate的区别
- 尚硅谷MySQL数据库高级学习笔记
- MySQL架构
- 事务部分
- MySQL知识点
- mysql索引
- Linux docker安装 mysql 8.0.25
- docker 安装mysql 5.7
- mysql Field ‘xxx’ doesn’t have a default value
- mysql多实例
- docker中的sql文件导入
- mysql进阶知识
- mysql字符集
- 连接的原理
- redo日志
- InnoDB存储引擎
- InnoDB的数据存储结构
- B+树索引
- 文件系统-表空间
- Buffer Pool
- 亿级数据导入到es
- MySQL数据复制
- MySQL缺少主键的表数据
- mysql update 其中更新的字段根据另一个更新字段作为条件去更新
- MySQL指定字段值排序(将指定值排在前面)
- 设置MySQL连接数、时区
- Navicat15右键删除数据刷新就又恢复了
- MySQL替换字段部分内容
- Java和MySQL统计本周本月本季和年
- 分页时order by 排序数据重复,丢失
- mysql同一张表根据某个字段删除重复数据
- mysqldump定时全量热备
- 专题总结
- 事务
- MySQL事务
- spring事务
- spring事务本类调用
- spring事务传播行为
- spring事务失效问题
- 锁和Transactional注解一块使用的问题
- 数据安全
- 敏感数据
- SQL注入
- 数据源
- XSS
- 接口设计
- 缓存设计
- 限流
- 自定义注解实现根据用户做QPS限流
- 架构
- 高可用
- Java
- Unsatisfied dependency expressed through field ‘baseMapper‘
- mybatisplus多数据源
- 单个字母前缀的java变量
- spring
- spring循环依赖解决
- 事务@Transactional
- yml 文件配置信息绑定到java工具类的静态变量上
- @Configuration @Component 区别
- springboot启动yml文件报错
- spring方法重试注解Retryable
- spring读取yml集合数据
- spring自定义注解
- 获取resource下的图片资源
- 手机号和电话号的正则验证
- 获取字符串中的数字
- mybatis
- mybatis多参数添加数据并返回主键
- 统一异常处理
- 分组校验
- Java读取Python json.dumps 函数保存的redis数据
- springboot整合springCache
- 若依mybatis值为null的字段没有返回
- 若依
- 接口白名单
- @JsonFormat时区问题
- RequestParam.value() was empty on parameter 0
- jdk8和hutool请求第三方的https报错
- springMVC
- springMVC与vue使用post传数组
- elementUI 时间组件报错问题
- vue具名插槽slot
- springboot配置maven的profiles(配置微服务多环境切换打包)
- resources 配置文件读取顺序
- Windows的cmd部署jar注意事项
- Java基础
- JUC(锁-并发-线程池)
- CAS
- Java 锁简介
- synchronized和Logk有什么区别?用新的ock有什么好处
- synchronized锁介绍
- CompletableFuture
- 多线程
- 线程池
- 集合类
- map见过的小问题
- 退出双层循环
- StringBuilder和StringBuffer核心区别
- 日志打印
- 打印log日志
- log日志文件生成配置
- 日期时间
- 时间戳转为时间
- 并发工具
- 连接池
- http调用
- 内网访问天地图
- 判等问题
- 数值计算
- null问题
- 异常处理
- 文件IO
- 序列化
- 内存溢出OOM
- 子线程的错误, 全局异常处理捕获不到
- vue同一个项目访问多个不同ip地址接口
- Autowired注解导入为null
- shiro
- UnavailableSecurityManagerException错误
- Windows服务器80端口被占用
- java图片增加水印
- springcloud
- Feign方法配置错误导致jar包启动失败
- feign调用超时
- 定时任务quartz
- JavaPOI导出Excel
- 合并行和列
- 设置样式
- 设置背景色
- docker
- Linux 安装
- docker命令
- docker网络
- docker数据卷
- dockerfile
- docker安装ping命令
- docker-compose
- docker-compose文件内容介绍
- Linux关闭docker开机启动
- jar打包为镜像
- 迁移docker容器存储位置
- Nginx
- Linux在线安装Nginx
- nginx.conf 核心配置文件
- vue 和 nginx 刷新页面会报404
- nginx 转发给三个集群的tomcat
- ServerName匹配规则
- Nginx负载均衡策略
- location 匹配规则
- Nginx 搭建前端调用后台接口的集群
- alias与root
- nginx 拦截 post 请求, 带参数转发到前端页面
- 防盗链配置
- Nginx的缓存
- 通用Nginx配置
- nginx配置文件服务器
- 后台jar包得不到正确ip,nginx代理时要处理
- 升级使用websocket协议
- 设置IP黑/白名单
- Redis
- 缓存数据一致性
- 内存淘汰策略
- Redis数据类型
- gmt6
- Linux安装GMT6
- GMT6配置中文
- GMT文件修改Windows版本到Linux版本
- 注意GMT不同字体导致符号不同的问题
- GMT绘制南海诸岛小图
- GMT生成中文图例
- elasticsearch
- 安装配置
- Linux安装配置elasticsearch7.6.2
- Linux 安装 kibana 7.6.2
- 安装7.6.2中文分词器
- docker 安装elasticsearch7.6.2
- 安装Logback7.6.2
- springboot使用
- 0. elasticsearch账号密码模式访问
- 1. 配置连接
- 2. 索引
- 3. 批量保存更新
- Result window is too large 10000
- elasticsearch 分词的字段做排序 fielddata, 设置fielddata=true 无效果
- elasticsearch 完全匹配查询(精确查询)
- 模糊搜索
- 日期区间查询
- 6.x基础知识
- 自定义词库
- elasticsearch集群
- 搜索推荐Suggester
- 查询es保存的数组
- 亿级mysql数据导入到es
- es 报错 ORBIDDEN/12/index read-only
- es核心概念
- es的分布式架构原理
- 优化大数据量时的ES查询性能
- canal
- 1. mysql的Binlog
- 2. Canal 的工作原理
- 3. canal同步es
- JVM
- 1 类的字节码
- 2. 类的加载
- JVM知识点
- Maven
- 依赖冲突
- xxl-job
- docker 安装配置 xxl-job
- idea
- springboot启动报错命令过长
- services统一启动微服务各模块
- 云服务器安装宝塔面板
- 突然出现启动或者运行特别慢
- 有导入依赖但是显示红色同时点击进去也有依赖
- Linux
- sh文件执行报错: command not found
- 使用vagrant安装虚拟机
- Linux 开启端口
- 开放端口
- 复制文件夹及其文件到另一个文件夹
- 两个服务器之间映射端口
- TCP协议
- 分层模型
- TCP概述
- 支撑 TCP 协议的基石 —— 首部字段
- 数据包大小对网络的影响 —— MTU 与 MSS 的奥秘
- 端口号
- 三次握手
- TCP 自连接
- 四次挥手
- TCP 头部时间戳
- 分布式
- 分布式脑裂问题
- 分布式事务
- 基础知识
- 实现分布式事务的方案
- 阿里分布式事务中间件seata
- 幂等性问题
- 其他工具
- webstorm git提交代码后project目录树不显示
- 消息队列
- 如何保证消费的顺序
- 数据结构
- 漫画算法:小灰的算法之旅