MySQL 事务隔离机制与实现原理详解(MySQL 专栏启动)
本文导读本文介绍 MySQL 事务的使用和其存在长事务的风险, MySQL 的事务及其特性,并发事务带来的问题、事务的隔离级别与演示,单版本控制锁以及多版本并发控制 MVCC。
一、MySQL 事务使用 1、什么是事务事务是作为单个逻辑工作单元执行的一系列操作。这些操作要么全部,要么没有,是一个不可分割的工作单元。
例如,购买东西支付的过程中会有一系列操作,如查询余额、加减、更新余额。这些操作必须是同时的。否则,显示你支付成功,但是系统没有收到钱
在 MySQL 中,事务支持是在引擎层实现的,MyISAM 引擎不支持事务
2、事务的四个特性要成为事务,逻辑工作单元必须满足关系数据库管理系统中的四个特征。
即所谓的 ACID:原子性、一致性、隔离性、持久性。
原子性:事务的所有操作要么完成要么未完成,不会在中间阶段结束。
一致性:在事务之前和之后,数据库的完整性限制不会被打破。
隔离性:当多个事务同时访问数据库中的同一数据时,将显示关系。
持久性:事务完成后,事务所做的更改将持久保存,不会丢失。
ACID 需要通过 Redo 和 Undo 日志来保证。MySQL 日志系统详解:(后续补充)
3、MySQL 事务使用 MySQL 的事务启动方式有以下 2 种:
3.1、显式启动事务语句 begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
BEGIN -- 开启事务 START TRANSACTION -- 开启事务
INSERT INTO fork_business_detail VALUES ( 4, '123', '123', '123004', '2022-11-12 17:17:29', '1', '2022-11-12 17:17:37', '1' );
COMMIT -- 提交事务
ROLLBACK -- 回滚事务 3.2、关闭自动提交 set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。(不建议使用!)
set autocommit=0 -- 关闭自动提交
INSERT INTO fork_business_detail VALUES ( 4, '123', '123', '123004', '2022-11-12 17:17:29', '1', '2022-11-12 17:17:37', '1' );
COMMIT -- 提交事务
ROLLBACK -- 回滚事务二、MySQL 事务隔离性与隔离机制当在数据库上同时执行多个事务(可以理解为多任务、并发场景)时,可能会发生脏读、不可重复读和幻读。
1、四种隔离级别为了解决这些问题,MySQL 引入了“隔离级别”的概念。
隔离级别越高,效率就越低,很多时候,我们必须在两者之间找到平衡。SQL 标准的事务隔离级别包括:读(取)未提交(read uncommitted)、读(取)已提交(read committed)、可重复读取(repeatable read)和串行化(serializable )。
读未提交(RU,Read Uncommitted),它可以读取事务的中间过程。它违反了 ACID 特性,并且存在脏读取的问题。因此基本上不被使用,可以被忽略。
读已提交(RC,Read Committed),这意味着,如果已经提交了其他事务,我们可以看到这也是最常用的级别。然而,由于一些历史原因,RC 可能在生产环境中使用不多。
可重复读(RR,Repeatable Read)是目前使用最广泛的级别。它的特点是间隙锁定,这仍然是默认级别。在这个级别,死锁、低并发和其他问题经常发生。
串行化(serializable ),不是多版本实现,而是单版本实现,因为它的所有实现都是通过锁实现的。基本上不被使用,可以被忽略。
2、并发事务引起的问题脏读:事务 A 读取事务 B 更新的数据,然后 B 回滚,因此 A 读取的数据是脏的(实际上不应该存在的数据)
不可重复读:事务 A 多次读取相同的数据。事务 B 在事务 A 多次读取的过程中更新并提交数据,导致事务 A 多次捕获同一数据时结果不一致。
一个事务读取到其他事务已提交的数据导致前后两次读取数据不一样的情况
幻读:A 查出来数据,此时 B 提交,A 再次查同一数据时结果不一致。一个事务前后两次读取的数据不一致,是因为其他事务插入数据导致的事务并发情况
不可重复读和幻读很容易混淆,不可重复读侧重于修改,而幻读侧重于添加或删除。要解决不可重复读取的问题,只需要锁,符合条件的行,而要解决幻读问题需要锁表。
3、隔离级别问题剖析与演示
3.1 查看 mysql 事务隔离级别 SELECT @@transaction_isolation; -- 查看 mysql 事务隔离级别 SELECT @@tx_isolation; -- 查看 mysql 事务隔离级别 3.2、脏读问题将事务隔离级别修改为读未提交,可以看到,事务还没有提交,这时候去查询这条数据,发现数据已经可见了。
set session transaction isolation level read uncommitted; -- 设置成读未提交 SELECT @@tx_isolation; -- 查看 mysql 事务隔离级别
START TRANSACTION -- 事务 AINSERT INTO fork_business_detail VALUES ( 4, '123', '123', '123004', '2022-11-12 17:17:29', '1', '2022-11-12 17:17:37', '1' );ROLLBACK
select * from fork_business_detail where id= 4 -- 事务 B
3.3、不可重复读一个事务读取到其他事务已提交的数据导致前后两次读取数据不一样的情况。
select * from fork_business_detail where id= 4;
BEGIN; -- 开启事务 select * from fork_business_detail where id= 4;UPDATE fork_business_detail set SUB_ODR_ID=123004 where id= 4;COMMIT;
select * from fork_business_detail where id= 5;三、MySQL 事务实现原理 1、单版本控制——锁 serializable ,使用锁独占方式来确保只有一个版本时事务被隔离,因此锁可以理解为单版本控制。
在 MySQL 事务中,锁的实现与隔离级别有关。在 RR(Repeatable Read)隔离级别下,MySQL 使用间隙锁来防止以并行性为代价写入数据,以解决虚拟读取的问题。
这种类型的锁通常会导致死锁,因为它没有足够的并行性和许多冲突。现在流行的 Row 模式可以避免许多冲突甚至死锁,因此建议默认使用 Row+RC(Read Committed)模式隔离级别,这可以大大提高数据库的读写并行性。
2、多版本控制 MVCC 多版本控制,也称为 MVCC,是指数据的多版本处理,以实现数据库中的高度并发数据访问,以及事务的可见性,以确保事务可以看到其应该看到的数据版本。
如何生成多个版本?
每次修改数据库时,撤消( Undo log)日志都会记录当前修改记录的事务号和修改前数据状态的存储地址(即 ROLL_PTR),以便在必要时回滚旧数据版本。
例如,读取事务查询当前记录,但最近的事务尚未提交。根据原子性,读取事务无法看到最新的数据,但您可以在回滚段中找到旧版本数据,从而生成多个版本。
多版本控制巧妙地将独占和独占的稀有资源转换为并发,大大提高了数据库吞吐量和读/写性能。
这个 undo log 撤消日志和 redo log 重做日志和 binlog 归档日志,博主希望在,MySQL 日志系统详解一文中讲解:(后续补充)
MVCC 多版本实现,博主希望在,MySQL MVCC 工作原理与实现详解一文中讲解:(后续补充)
总结本文介绍 MySQL 事务的使用和其存在长事务的风险, MySQL 的事务及其特性,并发事务带来的问题、事务的隔离级别与演示,单版本控制锁以及多版本并发控制 MVCC。
评论