✧(≖ ◡ ≖✿
目录
本文系统阐述SQL事务的本质及其四大特性(原子性、隔离性、一致性、持久性),结合MySQL实践,解析事务的提交机制、隔离级别(读未提交、读已提交、可重复读、串行化)及MVCC机制。重点说明RR与RC隔离级别的差异源于ReadView生成时机不同,揭示脏读、幻读等现象成因,并阐释undo日志、隐藏字段与快照读如何协同实现高效并发控制,提升数据库性能与数据一致性。
SQL中的事务本质是为了寻找并发与效率的平衡点
事务认识
角度:普通用户+并发层
事务(transaction)是 SQL语句中像“insert” “update” “delete” . . . 等组成的一个集合,用于处理某项任务像:将新生大一“电子信息工程班”入库。
既然事务是站在“整体/用户”的角度执行的所以只有两种结果:要么成功,要么失败 --- 要么全部成功,要么全部失败。
事务性质
理解事务的性质必须区分“事务前、事务中、事务后”三种状态,站在“事务中”的角度更加容易理解。
原子性(Atomic):一个事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(rollback)到事务开始前的状态,就像此事务从来没有执行过一样。
隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据不一致。事务的隔离分为不同级别包括读未提交(Read Uncommitted)、读提交(Read Committed)可重复读(Repeatable Read)、串行化(Serializable)。
一致性(Consistency):在事务开始之前和结束之后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发地完成预定的工作。
持久性(Durability):事务处理后对数据的修改就是持久的,即使系统故障也不会丢失。
提交方式
章节总揽定位:
MySQL数据库基础课
14 1:05:00 在一个begin内与autocommit相关的出现异常/正常提交的MySQL行为探讨(帮助理解:提交是什么 事务全部完成/全部未完成/事务中)。(必看)
手动提交、自动提交
手动提交:
-- 手动开启一个事务 其实是模拟事务行为
begin;
-- 事务内的操作
insert . . .
insert . . .
update . . .
-- 提交
commit;
-- 事务结束
自动提交:
MySQL自动控制的提交行为(兜底行为),通过 show variables like 'autocommit' 来查看情况
(默认开启)

隔离级别
读未提交(Read Uncommitted):链接数据库的多端实时受对端事务中(未commit)的影响。
读提交(Read Committed):commit后对端才出现变化。
可重复读(Repeatable Read):MySQL默认隔离级别,事务中读取永远保持一致(而不受commit影响)。
串行化(Serializable):最高程度隔离级别(A端要insert,B端事务中还未commit,A端直接阻塞(超时中断)加锁,保护数据。
事务隔离级别的设置与查看
事务隔离级别的查看
默认是可重复读(Repeatable Read)
global

session

global与session的辨识
它们二者是为了隔离级别的解耦而设计的。
自登入mysql就称一个session退出后session结束,session隔离级别默认与global一致。
我们在session内可以设置session与global的隔离级别
1.设置global后session无法自动同步(必须重登)。
2.设置session后仅影响当前一次session,重登session依然默认为global。
奇怪的读现象
如果你通过定义理解不了说明你复习的不够充分
脏读
读到了别人端未来得及commit 的数据。(情景:一定是在Read Uncomitted的隔离级别)
分析:这是不合理的现象,因为事务还未完全呢,你怎么读取到了呢?
幻读
幻读(Phantom Read) 是指:在同一个事务内,两次执行同样的范围查询,第二次却看到了第一次没有的“新行”。
| 隔离级别 | 幻读 |
|---|---|
| READ UNCOMMITTED | 可能 |
| READ COMMITTED | 可能 |
| REPEATABLE READ | 标准定义允许,但 InnoDB 基本避免 |
| SERIALIZABLE | 避免 |
读-写
多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制。
它为事务分配单向增长的事务 ID,为每个修改保存一个版本,版本与事务 ID 关联,读操作只读该事务开始前的数据库的快照。所以 MVCC 可以为数据库解决以下问题:
在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。
同时还可以解决脏读、幻读、不可重复读等事务隔离问题,但不能解决更新丢失问题。
理解 MVCC 需要知道三个前提知识:
-
3 个记录隐藏字段
-
undo 日志
-
Read View
3 个记录隐藏列字段:
-
DB_TRX_ID:6 byte,最近修改(修改/插入)事务 ID,记录创建这条记录/最后一次修改该记录的事务 ID。
-
DB_ROLL_PTR:7 byte,回滚指针,指向这条记录的上一个版本(简单理解成,指向历史版本就行,这些数据一般在 undo log 中)。
-
DB_ROW_ID:6 byte,隐含的自增 ID(隐藏主键),如果数据表没有主键,InnoDB 会自动以 DB_ROW_ID 产生一个聚簇索引。
补充:实际还有一个删除 flag 隐藏字段,即记录被更新或删除并不代表真的删除,而是删除 flag 变了。
| name | age | DB_TRX_ID (创建该记录的事务 ID) | DB_ROW_ID (隐式主键) | DB_ROLL_PTR (回滚指针) |
|---|---|---|---|---|
| 张三 | 28 | null | 1 | null |
undo 日志
所有机制:索引、事务、隔离性、日志等,都是在内存中完成的,即在 MySQL 内部的相关缓冲区中,保存相关数据,完成各种判断操作。然后在合适的时候,将相关数据刷新到磁盘当中的。
所以,我们这里理解 undo log,简单理解成,就是 MySQL 中的一段内存缓冲区,用来保存日志数据的就行。
快照(Read View)的形成

我关于快照下读-写的认识☆
写类:insert、update、delete 对象仅仅是最新版本。
读类:select 对象可以是最新,也可以是旧版本(这是因为“读”需求)。
正因为这个性质,产生了读相关的加锁问题:
-
最新版本加锁 ——> 串行读(Serialized)模式的产生。
-
非最新版本全部无需加锁 —— 这是因为“写”无法支持旧版本。(快照 read view 的产生)
struct readview { 的关键字段
m_ids:一张列表,用来维护 Read View 生成时刻,系统正活跃的事务 ID。
up_limit_id:记录 m_ids 列表中事务 ID 最小的 ID(没有写错)。(事务越新越大)
low_limit_id:Read View 生成时刻系统尚未分配的下一个事务 ID,也就是目前已出现过的事务 ID 的最大值 + 1(也没有写错)。
creator_trx_id:创建该 Read View 的事务 ID。
readview(快照)的形成是由select产生的。
RR 与 RC 的本质区别
正是 Read View 生成时机的不同,从而造成 RC、RR 级别下快照读的结果的不同。
在 RR 级别下,某个事务的对某条记录的第一次快照读会创建一个快照及 Read View,将当前系统活跃的其他事务记录起来。此后在调用快照读的时候,还是使用的是同一个 Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个 Read View,所以对之后的修改不可见;
即 RR 级别下,快照读生成 Read View 时,Read View 会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的。而早于 Read View 创建的事务所做的修改均是可见。
而在 RC 级别下,事务中,每次快照读都会新生成一个快照和 Read View,这就是我们在 RC 级别下的事务中可以看到别的事务提交的更新的原因。
总之,在 RC 隔离级别下,是每个快照读都会生成并获取最新的 Read View;而在 RR 隔离级别下,则是同一个事务中的第一个快照读才会创建 Read View,自此以后这个 Read View 不变,之后的快照读获取的都是同一个 Read View。
正是 RC 每次快照读,都会形成 Read View,所以,RC 才会有不可重复读问题。
感谢支持,长期连载
欢迎关注

转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/oooooooooooohd/article/details/166945172




