有错误的地方,欢迎大家评论留言指正!
一、事务的定义:
- 数据库事务可以包含一个或多个数据库操作,但这些操作构成一个逻辑上的整体。
- 构成逻辑整体的这些数据库操作,要么全部执行成功,要么全部不执行。
- 构成事务的所有操作,要么全都对数据库产生影响,要么全都不产生影响,即不管事务是否执行成功,数据库总能保持一致性状态。
- 以上即使在数据库出现故障以及并发事务存在的情况下依然成立。
二、事务的特征(ACID保证最终一致性):
- 原子性(Atomicity):事务是最小的执行单位,不允许分割。 整个事务中的所有操作要么全部提交成功,要么全部失败回滚;例如转账的这两个关键操作(将张三的余额减少200元,将李四的余额增加200元)要么全部完成,要么全部失败。undolog 保证原子性。
- 一致性(Consistency): 事务执行前后,数据库的状态应保持一致性。 确保从一个正确的状态转换到另外一个正确的状态,这就是一致性。例如转账业务中,将张三的余额减少200元,中间发生断电情况,李四的余额没有增加200元,这个就是不正确的状态,违反一致性。又比如表更新事务,一部分数据更新了,但一部分数据没有更新,这也是违反一致性的;
- 隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。 锁和 MVCC 保证隔离性。
- 持久性(Durability):一个事务被提交之后,对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。 redolog 保证持久性。
三、事务之间的相互影响:
- 脏读(Dirty Read):
指一个事务读取了另一个事务未提交的数据。 当事务T1正在访问字段 A 并且对进行了修改,而这种修改还没有提交到数据库中。这时另外一个事务 T2 也访问和使用字段 A,但由于事务 T1 修改字段 A 后还没有提交 COMMIT,而那么事务 T2 读到的字段 A 是“脏数据”。 - 第一类丢失更新:(脏写)
撤销一个事务的时候,把其它事务已提交的更新数据覆盖了。 这是完全没有事务隔离级别造成的。如果事务1被提交,另一个事务被撤销,那么会连同事务1所做的更新也被撤销。 - 第二类丢失更新:
当两个或多个事务查询相同的记录,然后各自基于查询的结果更新记录时会造成第二类丢失更新问题。 每个事务不知道其它事务的存在,最后一个事务对记录所做的更改将覆盖其它事务之前对该记录所做的更改。通过读加锁(for update)或者版本号解决。 - 不可重复读:(MySql默认隔离级别)
不可重复读取是指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。 如果事务1在事务2的更新操作之前读取一次数据,在事务2的更新操作之后再读取同一笔数据一次,两次结果是不同的。原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。 - 幻读:
指事务读取某个范围的数据时,因为其他事务的操作导致前后两次读取的结果不一致。 事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录,就好像发生了幻觉一样。
不可重复读重点在于update和delete,而幻读的重点在于insert。避免不可重复读需要锁行(某一行在select操作时,不允许update与delete)就行,避免幻读则需要锁表或间隙锁。
四、事务隔离级别:
在MySQL数据库中,支持下面四种隔离级别,默认的为Repeatable read (可重复读) ;而在 Oracle数据库 中,只支持Serializable (串行化) 级别和 Read committed (读已提交) 这两种级别,其中默认的为 Read committed(读已提交) 级别。
-
读未提交:
在读未提交级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读 。 -
读已提交(原理):
- 当前读(一致性锁定读):(就是给行记录加 X 锁)(默认)
- 如果是当前读,会对读取的记录加共享锁或排它锁,保证事务之间的隔离性。但是会出现幻读。
- 加锁情况:事务对当前被读取的数据加 行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
- 快照读(一致性非锁定读、MVCC):(就是单纯的 SELECT 语句)
- 读取的是数据的可见版本;如果读取的行正在执行 DELETE 或 UPDATE 操作(即读取的记录已被其它事务加上 X 锁),这时读取操作不是去等待行上锁的释放。而是读取行的一个快照数据。
- RC 隔离级别每次 select 查询前都生成一个Read View (m_ids 列表);所以读取的是已提交的最新版本数据;
- 当前读(一致性锁定读):(就是给行记录加 X 锁)(默认)
-
可重复读(原理):
-
当前读(一致性锁定读):(就是给行记录加 X 锁或 S 锁)
- 如果是当前读,会对读取的记录使用 Next-key Lock 临键锁来防止幻读。
如果两次查询中间有其它事务插入数据,就会产生幻读。所以,会对读取的记录使用 Next-key Lock 临键锁,来防止其他事务在间隙间插入数据。 - 加锁情况:事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加行级共享锁,直到事务结束才释放;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
- 如果是当前读,会对读取的记录使用 Next-key Lock 临键锁来防止幻读。
-
快照读(一致性非锁定读、MVCC):(就是单纯的 SELECT 语句)(默认)
- 和 RC 一样,读取的是数据的可见版本;如果读取的行正在执行 DELETE 或 UPDATE 操作(即读取的记录已被其它事务加上 X 锁),这时读取操作不是去等待行上锁的释放。而是读取行的一个快照数据。
- RR 隔离级别只会在事务开启后的第一次查询生成 Read View ,并使用至事务提交。
- 防止了可重复读和防止快照读下的 “幻读”(部分幻读)。
-
SELECT语句时,可以指定当前读方式。(非单纯的 SELECT)例如:
- SELECT … FOR SHARE / LOCK IN SHARE MODE:当前读方式,对记录加 S 锁,其它事务也可以加S锁,如果加 x 锁则会被阻塞。
- SELECT … FOR UPDATE / insert、update、delete:当前读方式,对记录加 X 锁,且其它事务不能加任何锁。
-
-
串行化:
以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待
五、并发事务的控制方式有哪些?
(MySQL 的隔离级别是基于锁实现的吗?)
MySQL 中并发事务的控制方式无非就两种:锁 和 MVCC。锁是悲观控制的模式,多版本并发控制是乐观控制的模式。
- 锁:
- 串行化隔离级别是通过锁来实现的,读已提交和可重复读隔离级别是基于 MVCC 实现的。不过,串行化之外的其他隔离级别可能也需要用到锁机制,就比如可重复读在当前读情况下需要使用加锁读来保证不会出现幻读。
- MVCC:
- MVCC 是多版本并发控制方法,通过创建数据的多个版本和使用快照读取来实现并发控制。读操作使用旧版本数据的快照,写操作创建新版本,并确保原始版本仍然可用。 这样,不同的事务可以在一定程度上并发执行,而不会相互干扰,从而提高了数据库的并发性能和数据一致性。
六、事务处理
- 不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。
- MySQL 默认是隐式提交:
- 每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。
- 通过 set autocommit=0 可以取消自动提交,直到 set autocommit=1 才会提交;autocommit 标记是针对每个连接而不是针对服务器的。
- 事务指令:
- START TRANSACTION - 指令用于标记事务的起始点。
- SAVEPOINT - 指令用于创建保留点。
- ROLLBACK TO - 指令用于回滚到指定的保留点;如果没有设置保留点,则回退到 START TRANSACTION 语句处。
- COMMIT - 提交事务。
参考:
原文连接:https://blog.csdn.net/qq_41116027/article/details/124135831
原文连接:https://blog.csdn.net/listeningsea/article/details/118407983
原文连接:https://javaguide.cn/database/mysql/mysql-questions-01.html
原文连接:https://javaguide.cn/database/sql/sql-syntax-summary.html