IT博文
MySQL 事务隔离级别详解
使用 docker compose 安装 tidb
架构师日记-如何写的一手好代码
生产事故-记一次特殊的OOM排查
Docker安装RabbitMQ——基于docker-compose工具
使用 docker-compose 部署单机 RabbitMQ
只需3步,即刻体验Oracle Database 23c
长达 1.7 万字的 explain 关键字指南!
Redis为什么能抗住10万并发?揭秘性能优越的背后原因
深度剖析Redis九种数据结构实现原理
【绩效季】遇到一个好领导有多重要,从被打差绩效到收获成长
为什么Redis不直接使用C语言的字符串?
Java阻塞队列中的异类,SynchronousQueue底层实现原理剖析
如何调整和优化 Go 程序的内存管理方式?
应用部署引起上游服务抖动问题分析及优化实践方案
Java 并发工具合集 JUC 大爆发!!!
卷起来!!这才是 MySQL 事务 & MVCC 的真相。
JDK8 到 JDK17 有哪些吸引人的新特性?
告别StringUtil:使用Java 11的全新String API优化你的代码
从JDK8飞升到JDK17,再到未来的JDK21
Java JMH Benchmark Tutorial
linux和macOS下top命令区别
Windows10关闭Hyper-V的三种方法
为什么应该选择 POSTGRES?
阿里云对象存储 OSS 限流超过阈值自动关闭【防破产,保平安】
Java高并发革命!JDK19新特性——虚拟线程(Virtual Threads)
“请不要在虚拟机中运行此程序”的解决方案
Spring中的循环依赖及解决
浅谈复杂业务系统的架构设计 | 京东云技术团队
面试题:聊聊TCP的粘包、拆包以及解决方案
操作日志记录实现方式
字节跳动技术团队-慢 SQL 分析与优化
Spring Boot 使用 AOP 防止重复提交
Controller层代码就该这么写,简洁又优雅!
SpringBoot 项目 + JWT 完成用户登录、注册、鉴权
重复提交不再是问题!SpringBoot自定义注解+AOP巧妙解决
SpringBoot 整合 ES 实现 CRUD 操作
SpringBoot 整合 ES 进行各种高级查询搜索
SpringBoot操作ES进行各种高级查询
SpringBoot整合ES查询
如何做架构设计? | 京东云技术团队
最值得推荐的五个VPN软件(便宜+好用+稳定),靠谱的V2ray梯子工具
我说MySQL每张表最好不超过2000万数据,面试官让我回去等通知?
vivo 自研鲁班分布式 ID 服务实践
使用自带zookeeper超简单安装kafka
推荐 6 个很牛的 IDEA 插件
喜马拉雅 Redis 与 Pika 缓存使用军规
「程序员转型技术管理」必修的 10 个能力提升方向
jdk17 下 netty 导致堆内存疯涨原因排查 | 京东云技术团队
如何优雅做好项目管理?
MySQL 到 TiDB:Hive Metastore 横向扩展之路
聊聊即将到来的 MySQL5.7 停服事件
Linux终端环境配置
微软 Edge 浏览器隐藏功能一览:多线程下载、IE 模式、阻止视频自动播放等
Hutool 中那些常用的工具类和实用方法
clash 内核删库?汇总目前常用的内核仓库和客户端
JDK11 升级 JDK17 最全实践干货来了 | 京东云技术团队
我是如何写一篇技术文的?
虚拟线程原理及性能分析
Java线程池实现原理及其在美团业务中的实践
Editplus和EmEditor配置一键编译java运行环境
用Spring Boot 3.2虚拟线程搭建静态文件服务器有多快?
SpringBoot中使用LocalDateTime踩坑记录 - 程序员偏安 - 博客园
程序员必备!10款实用便捷的Git可视化管理工具 - 追逐时光者 - 博客园
基于Netty开发轻量级RPC框架
开发Java应用时如何用好Log
复杂SQL治理实践 | 京东物流技术团队
火山引擎ByteHouse:分析型数据库如何设计并发控制?
多次崩了之后,阿里云终于改了
推荐程序员必知的四大神级学习网站
初探分布式链路追踪
新项目为什么决定用 JDK 17了
Java上进了,JDK21 要来了,并发编程再也不是噩梦了
mapstruct这么用,同事也开始模仿
再见RestTemplate,Spring 6.1新特性:RestClient 了解一下!
【MySQL】MySQL表设计的经验(建议收藏)
如何正确地理解应用架构并开发
解读工行专利CN112905176B
工商银行取得「基于 Spring Boot 的 web 系统后端实现方法及装置」专利
IDEA 2024.1:Spring支持增强、GitHub Action支持增强、更新HTTP Client等
TIOBE 2 月:Go 首次进入前十、“上古语言” COBOL 和 Fortran 排名飙升
Java 21 虚拟线程如何限流控制吞吐量
🎉 通用、灵活、高性能分布式 ID 生成器 | CosId 2.6.6 发布
20年编程,AI编程6个月,关于Copliot辅助编码工具,你想知道的都在这里
Java 8 内存管理原理解析及内存故障排查实践
消息队列选型之 Kafka vs RabbitMQ
从 MongoDB 到 PostgreSQL 的大迁移
腾讯云4月8日故障复盘及情况说明
PHP 在 2024 年还值得学习吗?
AMD集显安装显卡驱动之后出现黑屏,建议这样解决
使用 Docker 部署 moments 微信朋友圈 - 谱次· - 博客园
Java 17 是最常用的 Java LTS 版本
盘点Lombok的几个骚操作
Llama 3 + Ollama + Open WebUI打造本机强大GPT
如何优雅地编写缓存代码
Gmeek快速上手
笔记软件思源远程和本地接入大语言模型服务Ollama实现AI辅助写作(Windows篇)
Git Subtree:简单粗暴的多项目管理神器
这款轻量级规则引擎,真香!!
Ollama教程:本地LLM管理、WebUI对话、Python/Java客户端API应用
GLM-4-9B支持 Ollama 部署
智谱AI开源代码生成大模型第四代版本:CodeGeeX4-ALL-9B
美团二面:如何保证Redis与Mysql双写一致性?连续两个面试问到了!
免费开源好用,Obsidian和Omnivore真正实现一键联动剪藏文章,手把手教程!
得物 Redis 设计与实践
架构图怎么画?手把手教您,以生鲜电商为例剖析业务/应用/数据/技术架构图
使用Hutool要注意了!升级到6.0后你调用的所有方法都将报错 - 掘金
别再用雪花算法生成ID了!试试这个吧
无敌的Arthas!
Navicat Premium v16、v17 破解激活
🎉 分布式接口文档聚合,Solon 是怎么做的?
深入体验全新 Cursor AI IDE 后,说杀疯了真不为过!
Nacos 3.0 架构全景解读,AI 时代服务注册中心的演进
本文档使用 MrDoc 发布
-
+
MySQL 事务隔离级别详解
## MySQL 事务隔离级别 SQL:1992 标准定义了四种事务隔离级别,分别是读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。不同的隔离级别下会产生脏读、幻读、不可重复读等相关问题,因此在选择隔离级别的时候要根据应用场景来决定,使用合适的隔离级别。 MySQL 中的 InnoDB 存储引擎提供了 SQL 标准所描述的四种事务隔离级别,默认事务隔离级别是可重复读(Repeatable Read)。 各种隔离级别和数据库事务并发时存在的问题对应情况如下: | 隔离级别 | 脏读 | 不可重复读 | 幻读 | | --- | --- | --- | --- | | Read Uncommitted | √ | √ | √ | | Read Committed | × | √ | √ | | Repeatable Read | × | × | √ | | Serializable | × | × | × | - 读未提交(Read Uncommitted)允许脏读,就是在该隔离级别下,可能读到其他会话未提交事务修改的数据,存在脏读、不可重读读、幻读的问题。 - 读已提交(Read Committed)只能查询到已提交的数据。这是 Oracle 数据库默认的事务隔离级别。存在不可重读读、幻读的问题。 - 可重复读(Repeatable Read)就是在一个事务里相同条件下,无论何时查到的数据都和第一次查到的数据一致。这是 MySQL 数据库 InnoDB 引擎默认的事务隔离级别。在范围查询时存在幻读的问题。 - 串行化(Serializable)是最高的事务隔离级别,它严格服从 ACID 特性的隔离级别。所有的事务依次逐个执行,事务之间互不干扰,该级别可以防止脏读、不可重复读以及幻读。但每个事务读数据时都需要获取表级的共享锁,导致读和写都会阻塞,性能极低。 > 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是**读已提交(Read Committed)**,但是 MySQL 中 InnoDB 存储引擎默认使用**可重复读(Repeatable Read)**并不会有任何性能损失。 ## 如何设置 MySQL 事务隔离级别 1、可以在 MySQL 的配置文件 my.cnf、my.ini 中设置: transaction-isolation\=READ-UNCOMMITTED # 读未提交 或 transaction-isolation\=READ-COMMITTED # 读已提交 或 transaction-isolation\=REPEATABLE-READ # 可重复读 或 transaction-isolation\=SERIALIZABLE # 串行化 2、可以在连接 MySQL 服务端命令行用 --transaction-isolation; 3、可以使用 SET TRANSACTION 命令改变单个或者所有新连接的事务隔离级别: SET \[SESSION | GLOBAL\] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE} \# 如 set session transaction isolation level read committed; - 不带 GLOBAL 或 SESSION 关键字表示设置下一个事务的隔离级别; - 使用 GLOBAL 关键字表示对全局设置事务隔离级别,设置后的事务隔离级别对所有新的数据库连接生效; - 使用 SESSION 关键字表示对当前的数据库连接设置事务隔离级别,只对当前连接生效; - 任何客户端都可以自由改变当前会话的事务隔离级别,可以在事务中间改变,也可以改变下一个事务的隔离级别。 ## 如何查看 MySQL 事务隔离级别 \# MySQL 8.0 之前 SELECT @@global.tx\_isolation; SELECT @@session.tx\_isolation; SELECT @@tx\_isolation; \# MySQL8.0 SELECT @@global.transaction\_isolation; SELECT @@session.transaction\_isolation; SELECT @@transaction\_isolation; > 在 MySQL 8.0 之前的版本中 `tx_isolation` 是作为 `transaction_isolation` 的别名被应用的,新版本已经弃用了,需要把 `tx_isolation` 换成 `transaction_isolation`,否则会出现 `1193 - Unknown system variable 'xx_isolation'` 错误。 ## MySQL 事务隔离级别实践 在 MySQL 中创建一个 test 数据库,在 test 数据库中创建一个 account 账户数据表作为测试使用: CREATE TABLE \`account\` ( \`id\` int NOT NULL AUTO\_INCREMENT COMMENT '账户id', \`name\` varchar(32) COLLATE utf8\_bin DEFAULT NULL COMMENT '姓名', \`balance\` int DEFAULT '0' COMMENT '余额', PRIMARY KEY (\`id\`) ) ENGINE\=InnoDB DEFAULT CHARSET\=utf8 COLLATE\=utf8\_bin; 在 account 表中插入测试数据: INSERT INTO test.account (name, balance) VALUES ('熊大',300), ('熊二',400), ('光头强',500) 此时熊大、熊二、光头强的账户余额分别为 300 元、400 元、500元。 ### 读未提交(read uncommitted) 开启两个终端分别为 A 和 B,登录 MySQL,并将当前终端的事务隔离级别设置为读未提交 read uncommitted: mysql\> set session transaction isolation level read uncommitted; Query OK, 0 rows affected (0.01 sec) mysql\> select @@session.transaction\_isolation; +---------------------------------+ | @@session.transaction\_isolation | +---------------------------------+ | READ\-UNCOMMITTED | +---------------------------------+ 1 row in set (0.00 sec) 在终端 A 开启事务并查询熊二的账户余额: mysql\> begin; Query OK, 0 rows affected (0.00 sec) mysql\> select \* from account where id \= 2; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 2 | 熊二 | 400 | +----+--------+---------+ 1 row in set (0.00 sec) 此时熊二的账户余额为 400 元。 在终端 B 开启事务并向熊二的账户余额添加 50 元: mysql\> begin; Query OK, 0 rows affected (0.01 sec) mysql\> update account set balance \= balance + 50 where id \= 2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 此时在终端 A 查询熊二的账户余额: mysql\> select \* from account where id \= 2; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 2 | 熊二 | 450 | +----+--------+---------+ 1 row in set (0.00 sec) 可以发现,终端 A 事务读取到了终端 B 事务还未提交的数据,这个问题就是**脏读**。终端 A 事务两次读取的数据不一致,这个问题就是**不可重复读**。 如果此时终端 B 事务回滚,而终端 A 事务对熊二账户余额进行减 50 元,结果会是什么样? 终端 B 事务回滚并查询到余额为 400 元: mysql\> rollback; Query OK, 0 rows affected (0.01 sec) mysql\> select \* from account where id \= 2; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 2 | 熊二 | 400 | +----+--------+---------+ 1 row in set (0.00 sec) 终端 A 事务修改并提交,查询到余额为 350 元: mysql\> update account set balance \= balance \- 50 where id \= 2; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql\> commit; Query OK, 0 rows affected (0.02 sec) mysql\> select \* from account where id \= 2; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 2 | 熊二 | 350 | +----+--------+---------+ 1 row in set (0.00 sec) 以下是整个事件的时间线: | 时间线 | 终端 A 事务 | 终端 B 事务 | | --- | --- | --- | | ① | begin; | | | ② | select \* from account where id = 2; 读到熊二的账户余额为 400 元 | | | ③ | | begin; | | ④ | | update account set balance = balance + 50 where id = 2; | | ⑤ | select \* from account where id = 2; 读到熊二的账户余额为 450 元 **(脏读+不可重复读)** | | | ⑥ | | rollback; | | ⑦ | | select \* from account where id = 2; 读到熊二的账户余额为 400 元 | | ⑧ | update account set balance = balance - 50 where id = 2; | | | ⑨ | commit; | | | ⑩ | select \* from account where id = 2; 读到熊二的账户余额为 350 元 **(不受影响)** | | 那么脏读有什么影响呢? 在应用程序中,如果一个事务读到脏数据,并作为其他业务逻辑的依据或者进行其他处理,但其并不知道其他会话回滚了事务,那么后续的整个逻辑处理都可能存在问题。 ### 读已提交(read committed) 开启两个终端分别为 A 和 B,登录 MySQL,并将当前终端的事务隔离级别设置为读已提交 read committed: mysql\> set session transaction isolation level read committed; Query OK, 0 rows affected (0.00 sec) mysql\> select @@session.transaction\_isolation; +---------------------------------+ | @@session.transaction\_isolation | +---------------------------------+ | READ\-COMMITTED | +---------------------------------+ 1 row in set (0.00 sec) 在终端 A 开启事务并查询光头强的账户余额: mysql\> begin; Query OK, 0 rows affected (0.00 sec) mysql\> select \* from account where id \= 3; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 3 | 光头强 | 500 | +----+-----------+---------+ 1 row in set (0.00 sec) 此时光头强的账户余额为 500 元。 在终端 B 开启事务,为光头强的账户余额增加 100 元: mysql\> begin; Query OK, 0 rows affected (0.01 sec) mysql\> update account set balance \= balance + 100 where id \= 3; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 此时终端 A 事务再次查询光头强的账户余额,查询到余额仍为 500 元,**说明不存在脏读问题**: mysql\> select \* from account where id \= 3; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 3 | 光头强 | 500 | +----+-----------+---------+ 1 row in set (0.00 sec) 在终端 B 提交事务: mysql\> commit; Query OK, 0 rows affected (0.01 sec) 此时终端 A 事务再次查询光头强的账户余额,查询到余额为 600 元: mysql\> select \* from account where id \= 3; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 3 | 光头强 | 600 | +----+-----------+---------+ 1 row in set (0.00 sec) 可以发现终端 A 事务相同条件下两次查询的结果不一致,这个问题就是**不可重读读**。 以下是整个事件的时间线: | 时间线 | 终端 A 事务 | 终端 B 事务 | | --- | --- | --- | | ① | begin; | | | ② | select \* from account where id = 3; 读到光头强的账户余额为 500 元 | | | ③ | | begin; | | ④ | | update account set balance = balance + 100 where id = 3; | | ⑤ | select \* from account where id = 3; 读到光头强的账户余额为 500 元 **(不存在脏读)** | | | ⑥ | | commit; | | ⑦ | select \* from account where id = 3; 读到光头强的账户余额为 600 元 **(不可重复读)** | | | ⑧ | commit; | | ### 可重复读(repeatable read) 开启两个终端分别为 A 和 B,登录 MySQL,并将当前终端的事务隔离级别设置为可重复读 repeatable read: mysql\> set session transaction isolation level repeatable read; Query OK, 0 rows affected (0.00 sec) mysql\> select @@session.transaction\_isolation; +---------------------------------+ | @@session.transaction\_isolation | +---------------------------------+ | REPEATABLE\-READ | +---------------------------------+ 1 row in set (0.00 sec) mysql\> select \* from account; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 1 | 熊大 | 300 | | 2 | 熊二 | 350 | | 3 | 光头强 | 600 | +----+-----------+---------+ 3 rows in set (0.00 sec) 此时查询熊大、熊二、光头强的账户余额分别为 300 元、350 元、600 元。 在终端 A 开启事务并查询光头强的账户余额: mysql\> begin; Query OK, 0 rows affected (0.01 sec) mysql\> select \* from account where id \= 3; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 3 | 光头强 | 600 | +----+-----------+---------+ 1 row in set (0.00 sec) 此时光头强的账户余额为 600 元。 在终端 B 开启事务,为光头强的账户余额增加 100 元: mysql\> begin; Query OK, 0 rows affected (0.00 sec) mysql\> update account set balance \= balance + 100 where id \= 3; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 此时终端 A 事务再次查询光头强的账户余额,查询到余额仍为 600 元,**说明不存在脏读问题**: mysql\> select \* from account where id \= 3; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 3 | 光头强 | 600 | +----+-----------+---------+ 1 row in set (0.00 sec) 在终端 B 提交事务: mysql\> commit; Query OK, 0 rows affected (0.01 sec) 此时终端 A 事务再次查询光头强的账户余额,查询到余额仍为 600 元,**说明不存在不可重复读问题**:: mysql\> select \* from account where id \= 3; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 3 | 光头强 | 600 | +----+-----------+---------+ 1 row in set (0.00 sec) 以下是整个事件的时间线: | 时间线 | 终端 A 事务 | 终端 B 事务 | | --- | --- | --- | | ① | begin; | | | ② | select \* from account where id = 3; 读到光头强的账户余额为 600 元 | | | ③ | | begin; | | ④ | | update account set balance = balance + 100 where id = 3; | | ⑤ | select \* from account where id = 3; 读到光头强的账户余额为 600 元 **(不存在脏读)** | | | ⑥ | | commit; | | ⑦ | select \* from account where id = 3; 读到光头强的账户余额为 600 元 **(不存在不可重复读)** | | | ⑧ | commit; | | **但在可重复读的事务隔离级别下,仍然存在幻读问题。**下面我们来复现一下。 在终端 A 开启事务并查询 id 大于 2 的账户信息: mysql\> begin; Query OK, 0 rows affected (0.01 sec) mysql\> select \* from account where id \> 2; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 3 | 光头强 | 700 | +----+-----------+---------+ 1 row in set (0.00 sec) 可以看到查询得到的结果是 1 条数据。 在终端 B 开启事务,插入一条数据并提交: mysql\> begin; Query OK, 0 rows affected (0.00 sec) mysql\> insert into account (name,balance) values('吉吉国王',400); Query OK, 1 row affected (0.00 sec) mysql\> commit; Query OK, 0 rows affected (0.01 sec) 此时终端 A 再次查询 id 大于 2 的账户信息: mysql\> select \* from account where id \> 2; +----+-----------+---------+ | id | name | balance | +----+-----------+---------+ | 3 | 光头强 | 700 | +----+-----------+---------+ 1 row in set (0.00 sec) 得到的结果仍然是相同的 1 条数据。为什么?说好的幻读呢?其实这是 MySQL 为了提高性能,使用了基于乐观锁的 MVCC(多版本并发控制)机制来避免了幻读问题,但幻读问题仍然存在。 如果终端 A 事务执行把 id 大于 2 的账户余额都修改为 800: mysql\> update account set balance \= 800 where id \> 2; Query OK, 1 row affected (0.00 sec) Rows matched: 2 Changed: 1 Warnings: 0 mysql\> select \* from account where id \> 2; +----+--------------+---------+ | id | name | balance | +----+--------------+---------+ | 3 | 光头强 | 800 | | 4 | 吉吉国王 | 800 | +----+--------------+---------+ 2 rows in set (0.00 sec) 此时可以看到查询得到的结果是 2 条数据。 以下是整个事件的时间线: | 时间线 | 终端 A 事务 | 终端 B 事务 | | --- | --- | --- | | ① | begin; | | | ② | select \* from account where id > 2; 读到账户信息数据是 1 条 | | | ③ | | begin; | | ④ | | insert into account (name,balance) values('吉吉国王',400); | | ⑤ | | commit; | | ⑥ | select \* from account where id > 2; 读到账户信息数据是 1 条 | | | ⑦ | update account set balance = 800 where id > 2; | | | ⑧ | select \* from account where id > 2; 读到账户信息数据是 2 条 **(存在幻读)** | | | ⑨ | commit; | | 幻读无法通过行级锁来解决,需要使用串行化的事务隔离级别,但这种事务隔离级别会极大的降低数据库的并发能力。 ### 串行化(serializable) 开启两个终端分别为 A 和 B,登录 MySQL,并将当前终端的事务隔离级别设置为串行化 serializable: mysql\> set session transaction isolation level serializable; Query OK, 0 rows affected (0.01 sec) mysql\> select @@session.transaction\_isolation; +---------------------------------+ | @@session.transaction\_isolation | +---------------------------------+ | SERIALIZABLE | +---------------------------------+ 1 row in set (0.00 sec) 在终端 A 开启事务并查询 id 大于 2 的账户信息: mysql\> begin; Query OK, 0 rows affected (0.00 sec) mysql\> select \* from account where id \> 2; +----+--------------+---------+ | id | name | balance | +----+--------------+---------+ | 3 | 光头强 | 800 | | 4 | 吉吉国王 | 800 | +----+--------------+---------+ 2 rows in set (0.00 sec) 可以看到查询得到的结果是 2 条数据。 在终端 B 开启事务,插入一条数据: mysql\> begin; Query OK, 0 rows affected (0.01 sec) mysql\> insert into account (name,balance) values('蹦蹦',600); 1205 \- Lock wait timeout exceeded; try restarting transaction 可以看到,在终端 B 事务执行新增操作时,会发生阻塞,锁超时后会抛出 `1205 - Lock wait timeout exceeded; try restarting transaction` 错误,避免了幻读。可以通过 `select * from performance_schema.data_locks;` 查看事务的锁信息,从 `supremum pseudo-record` 获知,通过添加间隙锁解决幻读问题。此处本文不详细展开,后续单独讲解。 > **supremum pseudo-record** > > 相当于比索引中所有值都大,但却不存在索引中,相当于最后一行之后的间隙锁。 ## 总结 本节主要介绍了 MySQL 的事务隔离级别,通过实战演示讲解了不同隔离级别解决的问题以及存在的问题,从中我们可以初步了解 MySQL 事务的隔离机制是通过锁机制和 MVCC (多版本并发控制) 实现的。 下一篇文章我们主要介绍 MySQL 的锁机制,通过实践学习不同事务隔离级别下的加锁情况。 ## 参考 事务隔离级别:[dev.mysql.com/doc/refman/…](https://link.juejin.cn/?target=https%3A%2F%2Fdev.mysql.com%2Fdoc%2Frefman%2F8.0%2Fen%2Finnodb-transaction-isolation-levels.html) 作者:朝雾轻寒 链接:[https://juejin.cn/post/7136112451959848991](https://juejin.cn/post/7136112451959848991) 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
admin
2023年4月7日 17:47
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码