📫作者简介:小明java问道之路,专注于研究 Java/ Liunx内核/ C++及汇编/计算机底层原理/源码,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。
📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。
🏆 InfoQ签约作者、CSDN专家博主/后端领域优质创作者/内容合伙人、阿里云专家/签约博主、51CTO专家 🏆
🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~
本文导读
本文介绍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 -- 事务A
INSERT 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。
版权归原作者 小明java问道之路 所有, 如有侵权,请联系我们删除。