# 一、事务的基本要素(ACID)
**1、原子性(Atomicity):**
事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
**2、一致性(Consistency):**
事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
**3、隔离性(Isolation):**
同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
**4、持久性(Durability):**
事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
# 二、事务的并发问题
**1、脏读:**
事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
**2、不可重复读:**
事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
**3、幻读:**
系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
**小结**
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
# 三、MySQL事务隔离级别
| 事务隔离级别 | 脏读 | 不可重复读| 幻读 |
| --- | --- | --- | --- |
| 读未提交(read-uncommitted) | 是 | 是 | 是 |
| 读已提交(read-committed) | 否 | 是 | 是 |
| 可重复读(repeatable-read) | 否 | 否 | 是 |
| 串行化(serializable) | 否 | 否 | 否 |
# 四、用例子说明各个隔离级别的情况
**1、读未提交:**
(1)打开一个客户端A,并设置当前事务模式为read uncommitted(未提交读),查询表account的初始值:
![](https://img.kancloud.cn/4e/3c/4e3c20c6819f49055e7b9a3080b6a3bd_811x358.png)
(2)在客户端A的事务提交之前,打开另一个客户端B,更新表account:
![](https://img.kancloud.cn/5b/cc/5bcc1e0876edff3b43ed8b9c6b7b64da_843x447.png)
(3)这时,虽然客户端B的事务还没提交,但是客户端A就可以查询到B已经更新的数据:
![](https://img.kancloud.cn/e4/27/e427dc3ffc63cb47899b080ebf1f6747_703x592.png)
(4)一旦客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那客户端A查询到的数据其实就是脏数据:
![](https://img.kancloud.cn/39/c7/39c7d552af3eb62fe793580a9d9fee97_859x523.png)
**2、读已提交:**
(1)打开一个客户端A,并设置当前事务模式为read committed(读已提交),查询表account的所有记录:
![](https://img.kancloud.cn/79/b3/79b343919ef5bfeef232afeefd7bff18_840x354.png)
(2)在客户端A的事务提交之前,打开另一个客户端B,更新表account:
![](https://img.kancloud.cn/7f/e5/7fe5c8f8688f995641ec9bde6c4445b6_836x454.png)
(3)这时,客户端B的事务还没提交,客户端A不能查询到B已经更新的数据,解决了脏读问题:
![](https://img.kancloud.cn/c0/c3/c0c31fb15174688a8c324669ec11a73d_744x457.png)
(4)客户端B的事务提交
![](https://img.kancloud.cn/30/4f/304f77bd3ea8cbcc113fd12e80a51684_759x387.png)
(5)客户端A执行与上一步相同的查询,结果 与上一步不一致,即产生了不可重复读的问题
![](https://img.kancloud.cn/9a/d8/9ad8aedff899dc7c46bd8bb2740ad3bc_792x459.png)
**3、可重复读:**
(1)打开一个客户端A,并设置当前事务模式为repeatable read,查询表account的所有记录
![](https://img.kancloud.cn/c8/6b/c86beecd429582b0d7696a8ed5822d55_836x365.png)
(2)在客户端A的事务提交之前,打开另一个客户端B,更新表account并提交
![](https://img.kancloud.cn/ee/f9/eef9ff67396d722ddae8806fac8923d8_844x517.png)
(3)在客户端A查询表account的所有记录,与步骤(1)查询结果一致,没有出现不可重复读的问题
![](https://img.kancloud.cn/85/20/8520a09f6710d95cb613d9703134898b_845x452.png)
**4、串行化:**
(1)打开一个客户端A,并设置当前事务模式为serializable,查询表account的初始值:
![](https://img.kancloud.cn/2c/cf/2ccf2d8df630e4293648722de3afd1c9_1462x367.png)
(2)打开一个客户端B,并设置当前事务模式为serializable,插入一条记录报错,表被锁了插入失败,mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。
![](https://img.kancloud.cn/87/37/87377a688a811c097a53413f0ca9af97_1047x212.png)
(4)在客户端A,可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。
![](https://img.kancloud.cn/28/38/2838fc1bf163a6b7521a3b516b0f9e4d_988x513.png)
(5)重新打开客户端B,插入一条新数据后提交
![](https://img.kancloud.cn/f7/2f/f72f5216423ab1ac89c63e50baef8467_865x189.png)
6)在客户端A查询表account的所有记录,没有 查出 新增数据,所以没有出现幻读
![](https://img.kancloud.cn/31/1f/311f50668d0d7c26b8fd88645d75312c_963x386.png)
# 五、快照(一致性视图):
可重复读是在事务开始的时候生成一个当前事务全局性的快照。
而读提交则是每次执行语句的时候都重新生成一次快照。
**快照要遵循以下规则:**
1. 当前事务内的更新,可以读到;
2. 版本未提交,不能读到;
3. 版本已提交,但是却在快照创建后提交的,不能读到;
4. 版本已提交,且是在快照创建前提交的,可以读到;
**主要区别:**
可重复读仅在事务开始是创建一次,而读提交每次执行语句的时候都要重新创建一次。
# 六、补充:
1、事务隔离级别为读已提交时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;
如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时,读写数据都会锁住整张表
4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
# 七、总结:
1、读未提交和串行化基本上是不需要考虑的隔离级别,前者不加锁限制,后者相当于单线程执行,效率太差。
2、读已提交解决了脏读问题,行锁解决了并发更新的问题。并且 MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的。
- 开发语言
- java
- Java基础篇
- Java多线程篇
- 进程和线程的区别,进程间如何通信
- 什么是线程上下文切换
- 什么是死锁
- 死锁的必要条件
- Synchrpnized和lock的区别
- 什么是AQS锁
- 为什么AQS使用的双向链表
- 有哪些常见的AQS锁
- sleep()和wait()的区别
- yield()和join()区别
- Java线程池
- SpringBoot
- spring boot 项目开发常用目录结构
- Mybatis-Plus
- MyBatisPlus的CRUD操作
- Mybatis-Plus主键ID生成策略
- JVM
- JVM组成
- 字节码文件的组成
- 类的生命周期
- JVM、JRE和JDK
- arthas
- 使用阿里arthas不停机解决线上问题
- Java IO
- php
- 安装swoole
- composer部分
- windows安装composer
- composer PSR-4映射
- swoole部分
- swoole安装
- thrift部分
- linux下安装thrift
- PHP使用Thrift
- lnmp部分
- 架构的工作原理
- tp5框架生命周期
- zookeeper部分
- zookeeper安装
- sort
- TCP和UDP的区别
- 软件
- xdebug
- vscode+phpstudy+xdebug无法断点(踩坑记)
- Hyperf框架
- 注解
- 开发方案
- 抖音
- 抖音达人视频发布与统计
- 安全问题
- 微信
- 微信公众平台怎样实现用户点击链接向公众号发消息
- CDN加速OSS计费说明
- 程序设计
- 正则表达式
- 面向对象
- 设计模式
- 创建型模式
- 工厂模式
- 单例模式
- 结构型模式
- 适配器模式
- 行为型模式
- 策略模式
- 观察者模式
- 算法部分
- 位运算
- 排序算法
- 双指针
- 贪心算法
- 动态规划
- 二分查找
- 华为题库
- 技术栈
- mq
- MQ 的优势和劣势
- rabbitmq部分
- windows安装rabbitmq
- RabbitMQ 简介
- 工作模式
- 高级特性-消息可靠投递-confirm
- 高级特性-消息可靠投递-return
- 高级特性-Consumer Ack
- 高级特性-消费端限流
- 高级特性-TTL
- 高级特性-死信队列
- Centos7下安装rabbitmq
- 数据库
- MongoDB
- MongoDB 相关概念
- Mysql
- 索引总结
- MySQL架构图
- InnoDB和MyISAM的区别
- 索引设计与优化
- 悲观锁和乐观锁
- mysql如何解除死锁状态
- 查询慢
- 数据库主键的优缺点
- MySQL锁详解
- SQL语句分类
- 开查询账号
- 数据库迁移
- MySQL实战知识点
- mysql清理binlog日志
- 面试总结
- 事务隔离
- 聚集索引与非聚集索引
- B树和B+树
- docker
- docker-desktop安装的坑点
- docker在linux平台下安装
- Ubuntu安装Docker
- 常用命令
- 适用于 Linux 的 Windows 子系统没有已安装的分发版
- docker核心架构图
- docker安装lnmp环境
- docker安装redis
- dockerfile
- docker-compose
- linux
- Ubuntu 更换国内源
- centos
- 常用命令
- virtualbox
- 关于VirtualBox安装Ubuntu时界面显示不全,没有下一步选项
- linux复制当前目录到其子目录下
- 命令
- cat和>、>>
- crontab命令
- 查看当前目录的文件大小
- shell登录和非shell登录
- nginx
- 正向代理
- 反向代理
- 负载均衡
- 分割Nginx的access.log日志并保留30天一个月时长,自动删除多余的日志
- linux安装nginx
- git
- 生成秘钥
- 常用命令
- Linux中git保存用户名密码
- git清除账号密码
- 设置git store 存储账号密码
- git submodule 使用小结
- 微服务
- 微服务技术栈
- nacos
- Nacos服务分级存储模型
- Nacos配置管理-配置热更新
- Nacos集群搭建
- 微服务保护
- 初识Sentinel
- 隔离和降级
- es
- DSL查询语法-相关性算法
- DSL查询语法-FunctionScoreQuery
- DSL查询语法-BooleanQuery
- 搜索结果处理-排序
- es深度分页问题
- 自动补全
- elasticsearch 设置密码
- redis
- redis简介
- 安装redis扩展
- redis数据类型
- redis常见问题
- PHP 使用 Redis 实现分布式锁
- 缓存更新策略
- [ Redis ] AOF 和 RDB 的相关介绍以及相关配置
- 分布式锁的8大坑
- 分布式锁-Redisson
- 内存回收
- UV统计
- Redis主从集群
- redis哨兵
- Redis安装目录下常见文件
- 通讯原理概述
- linux安装redis
- windows
- Win系统端口被占用
- Windows10 WSL2限制cpu和内存
- jekins
- 持续集成
- centos卸载gitlab
- jenkins搭配gitlab的webhook实现自动化部署
- 大数据
- Linux集群分发脚本xsync
- hadoop
- hadoop安装
- hadoop配置文件
- clickhouse
- ClickHouse 安装部署
- flink
- 数据仓库
- zookeeper
- zookeeper分布式安装
- ZK集群启动停止脚本
- kafka
- kafka分布式安装
- kafka集群启动停止脚本
- flume
- flume分布式安装
- Flume配置
- Flume使用
- maxwell
- Maxwell简介
- Maxwell部署
- Maxwell使用
- MaxwellBootstrapUtility - Connections could not be acquired from the underlying database
- 线上事故