目录

一 . 什么是事务?

二. 事务的操作

​三. 事务的四大特性

四 . 并发事务可能产生的问题

五 . 数据库的隔离级别


一 . 什么是事务?

从概念上来讲,事务是一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位 。

举个例子:假如 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');-- 回退到bROLLBACK TO b-- 继续回退 aROLLBACK TO a-- 如果这样,表示直接回退到事务开始的状态ROLLBACKCOMMIT 

这个过程当中,设置了两个保存点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 中提供了四种隔离级别来解决上述的并发事务问题,隔离级别越高,执行效率越低。

  1. 读未提交 :一个事务未提交时,但是他所做的变更就能够被其他事务所看到,即读未提交
  2. 读已提交: 一个事务提交之后,它所做的变更才能够被其他事务所看到,解决了脏读的问题
  3. 可重复读:同一个事务中,多次读取一个事务,获取到的结果是一致的, MySQL 的InnoDB引擎的默认隔离级别就是可重复读,但是没有解决幻读的问题。
  4. 串行化 :串行化会给记录加上读写锁,如果发生了读写冲突时,后访问的事务必须等待前一个事务执行完成,才能继续执行。

所以四种隔离级别的等级高低的排列如下 : 串行化 > 可重复读 > 读已提交 > 读未提交 ,隔离级别越低,事务请求的锁越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 实现原理和锁机制进行详细的讲解说明 .