目录
一 . 什么是事务?
从概念上来讲,事务是一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位 。
举个例子:假如 A 向 B 转账 100 元,先从 A 的账户中扣除100元,再在 B 的账户中增加 100 元,如果已经在 A 帐户中扣除100元,还没来得及增加B账户中的余额,此时银行系统发生故障,导致A的余额减少了,但是B的余额却没增加,所以就需要事务,将A的钱进行回滚。
二. 事务的操作
使用以下SQL语句来开启并提交事务
# 开启一个事务
START TRANSACTION;
#设置保存点
savepoint point1;
# 多条 SQL 语句
SQL1,SQL2...
#回退事务到保存点
rollback to point1
#回退全部事务
rollback
## 提交事务,有事务操作生效,不能回退
COMMIT;
下面给出一个具体的事务提交的案例:
-- 1.创建一张测试表
CREATE TABLE t27(
id INT,
`name` VARCHAR(32));
-- 2.开始事务
START TRANSACTION
-- 3.设置保存点
SAVEPOINT a
-- 执行dml操作
INSERT INTO t27 VALUES(100,'tom');
SELECT * FROM t27;
SAVEPOINT b
-- 执行dml操作
INSERT INTO t27 VALUES(200,'jack');
-- 回退到b
ROLLBACK TO b
-- 继续回退 a
ROLLBACK TO a
-- 如果这样,表示直接回退到事务开始的状态
ROLLBACK
COMMIT
这个过程当中,设置了两个保存点a , b ,可以回退到指定位置的保存点,也可以直接对事务进行回退,即不改变表的结构,事务设置完要进行 commit 提交。
三. 事务的四大特性
- 原子性 : 事务作为一个整体执行,包含在其中的事务要么全部都执行,要么全部都不执行
原子性的实现通过 undo log 日志来完成,当事务执行更新操作之前,数据库将当前数据行的旧值存储到 undo log 日志当中,如果事务执行过程中出现异常情况,就将事务进行回滚,InnoDB引擎就是利用 undo log 保存下来的记录,将数据恢复到事务开始之前。
- 隔离性:多个事务进行并发访问时,事务之间应该是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离
如果多个事务可以同时操作⼀个 数据,那么就会产⽣脏读、重复读、幻读的问题,MySQL中定义了四种隔离级别提供使用,隔离级别的底层实现是锁或者MVCC,但是屏蔽了加锁的细节。
- 持久性:事务完成进行提交时,对数据库所做的更改将永久性的保存在数据库中,即使数据库发生故障也不影响
当事务执行更新操作时 , 首先将数据的修改操作记录保存到Redo log 中, 而不是直接修改写入到磁盘的数据文件, 接着以顺序追加的方式写入磁盘,使得写入的操作非常高效, 当系统发生故障导致数据库重启时, MySQL 通过Redo log 来恢复为写入磁盘的数据库修改,重新执行这些操作,将数据持久化到磁盘,这样就保证了事务的持久性。
- 一致性 : 指的是事务开始之前和事务结束只有,数据不会被破坏,假如A账户给B账户转账100元,无论成功与否,A和B的总金额是不发生改变的
InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?
- 持久性是通过 redo log (重做日志)来保证的;
- 原子性是通过 undo log(回滚日志) 来保证的;
- 隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;
- 一致性则是通过持久性+原子性+隔离性来保证;
只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!
四 . 并发事务可能产生的问题
- 脏读问题
当事务 A 在对数据进行修改的过程中, 事务 B 对同一个数据进行 了读取,此时 B 的读操作就叫做脏读,读取到的数据也被称之为脏数据 。
- 幻读问题
在同一组数据当中,事务 A 进行查询数据 , 事务 B 进行 插入新的数据,导致事务 A 再次进行相同的查询时,查询到的数据行数发生了改变,这种情况称之为幻读,幻读的解决方案一般为加间隙锁。
- 不可重复读问题
在同一组数据中,事务 A 进行查询数据, 事务B进行修改数据,导致事务A 重新执行相同的查询时,查询到的内容发生了改变,这种情况就称之为不可重复读,不可重复读的解决方案为加行锁或者表锁。
在这其中,不可重复读和幻读都是并发事务当中容易出现的问题,但是幻读主要涉及的是数据的插入,而不可重复读涉及的是数据的修改和更新,幻读的结果是前后读取的数据的数量不一致,而不可重复读是前后读取到的数据的内容不一致。
其中严重性 : 脏读 > 不可重复读 > 幻读
五 . 数据库的隔离级别
为了规避以上的三种并发事务中可能遇到的问题,MySQL 中提供了四种隔离级别来解决上述的并发事务问题,隔离级别越高,执行效率越低。
- 读未提交 :一个事务未提交时,但是他所做的变更就能够被其他事务所看到,即读未提交
- 读已提交: 一个事务提交之后,它所做的变更才能够被其他事务所看到,解决了脏读的问题
- 可重复读:同一个事务中,多次读取一个事务,获取到的结果是一致的, MySQL 的InnoDB引擎的默认隔离级别就是可重复读,但是没有解决幻读的问题。
- 串行化 :串行化会给记录加上读写锁,如果发生了读写冲突时,后访问的事务必须等待前一个事务执行完成,才能继续执行。
所以四种隔离级别的等级高低的排列如下 : 串行化 > 可重复读 > 读已提交 > 读未提交 ,隔离级别越低,事务请求的锁越InnoDB 存储引擎默认使用 可重复读
MySQL 的隔离级别基于锁和 MVCC 机制共同实现的
- 设置并查看当前事务的隔离级别
-- 查看当前会话隔离级别
SELECT @@tx_isolation;
-- 查看系统当前隔离级别
SELECT @@ global.tx_isplation
-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
-- 设置系统当前隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL [设置你想设置的级别]
- MVCC
在MySQL InnoDB引擎层⾯,⼜有新的解决⽅案(解决加锁后读写性能问题),叫MVCC(Multi Version Concurrency Control) 多版本并发控制,它主要基于创建和维护数据的多个版本(或快照),以允许事务之间的并发执行而不会产生冲突。
对于InnoDB引擎,默认情况下会启用MVCC来处理并发事务。这意味着在没有显式设置的情况下,InnoDB引擎会使用MVCC机制来提供并发控制和事务隔离性。可以通过创建和管理数据的多个版本来实现。
在传统的锁定并发控制机制中,读操作和写操作之间会相互阻塞,因为写操作可能会改变数据,导致读操作的结果不一致。而MVCC通过创建数据的多个版本来解决这个问题。每个事务在开始时会创建一个一致性的数据库快照,然后在执行期间,事务只能看到在其开始之前已经存在的数据版本。这样,读操作可以并发地进行,而不会受到其他事务的写操作的影响 , 后续我们会对 MVCC 实现原理和锁机制进行详细的讲解说明 .