
目录
[Read View](#Read View)
存在问题
我们来看如下一个例子:

以上例子就是一张票卖了两份的例子。除此之外,还有银行取钱等例子。要解决上述问题,MySQL数据库的CURD操作得满足如下属性:
买票过程是原子的
买票互相不能影响
买完票应该永久有效
买前和买后都要是确定的状态
事务概念
事务就是一组DML语句组成,这些语句在逻辑上存在相关性,这一组DML语句要么全部成功,要么全部失败,是一个整体。事务是MySQL提供的一种机制,保证我们达到这样的效果。事务还规定不同的客户端看到的数据是不相同的。
事务特性
事务就是要做的或所做的事情,主要用于处理操作量大,复杂度高的数据,一个完整的事务,不仅仅是简单sql的集合,还要满足以下四个属性:
**原子性(Actomicity):**一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束再中间某个环节。事务在执行过程中发生错误,会被回滚(ROllback)到事务开始前的装态,就像这个事务从来没有执行过一样。
**一致性(Consistency):**在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包括资料的精确度、串联性以及后续数据库可以自发地完成预定的工作。
**隔离性(Isolation):**数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable)。
**持久性(Durability):**事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
以上四个属性,简称为ACID,但在Mysql中,并没有真正的一致性,一致性是通过其它三个来保证的。
事务的版本支持
查看MySQL中的事务版本支持如下所示:

事务提交方式
在MySQL中,事务的提交方式有常见的两种,分别为自动提交和手动提交,查看方式如下:

显示on表示自动提交。也可以手动修改,如下所示:

事务的基本操作
事务的基本操作如下:

回滚指的是在事务运行期间进行回滚,如图所示,事务开始后,可以设置保存点,当后续想要回滚时,使用rollback to即可将数据库回滚到对应的状态,若要结束事务,使用commit即可,此后数据便可持久化保存在数据库中。只要输入begin或者start transaction,事务便必须通过commit提交,才会持久化,与是否设置set autocommit无关。
若没有设置保存点但进行了rollback,则将之前的操作都抛弃掉了。同时,一旦事务被提交了就无法回滚。
若事务出现异常,MySQL会自动进行事务回滚,如下所示:

将autocommit设置为off,删除数据后异常退出,再查询数据,发现被删除的数据自动回滚,如下所示:

因此,autocommit会影响单独的sql语句,每一个sql就相当于一个事务。对于InnoDB每一条sql语句都会默认封装成事务,会自动提交。
事务的隔离级别
在数据库中,为了保证事务执行过程中尽量不受干扰,就有了一个重要的特征:隔离性;在数据库中,允许事务受不同程度的干扰,就有了一种重要的特征:隔离级别。
在事务场景中,隔离是必要的,隔离是运行中的事务之间,进行隔离。
隔离级别
MySQL中的常见隔离级别如下:
**读未提交(Read Uncommitted):**在该隔离级别,所有的事务都可以看到其他事务没有提交的执行结果。(实际生产中不可能使用这种隔离级别的),但是相当于没有任何隔离性,也会有很多并发问题,如脏读,幻读,不可重复读等。
**读提交(Read Committed):**该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默 认的)。它满足了隔离的简单定义:一个事务只能看到其他的已经提交的事务所做的改变。这种隔离 级别会引起不可重复读,即一个事务执行时,如果多次 select, 可能得到不同的结果。
**可重复读(Repeatable Read):**这是 MySQL 默认的隔离级别,它确保同一个事务,在执行 中,多次读取操作数据时,会看到同样的数据行。但是会有幻读问题。
**串行化(Serializable):**这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突, 从而解决了幻读的问题。它在每个读的数据行上面加上共享锁,。但是可能会导致超时和锁竞争 (这种隔离级别太极端,实际生产基本不使用)。
查看隔离级别如下:

global.transaction_isolation为全局隔离级别;session.transaction_isolation为当前会话的全局隔离级别,默认和全局隔离级别一样,设置这个只会影响当前的会话,不会影响其它会话;transaction_isolation和session.transaction_isolation是一样的。
设置隔离级别的语法如下:
sql
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ
COMMITTED | REPEATABLE READ | SERIALIZABLE}
如下所示;

改变隔离级别只会影响当前会话。
读未提交
读未提交示例如下所示:

从上图可以发现,一个事务在执行过程中,能读到另一个执行事务的更新(或其他操作)但是未commit的数据,这种现象叫做脏读。
读提交
读提交示例如下:

只有当一个事务更新完后commit了,另外一个事务才能看到更新的数据,在同一个事务内部,查询数据的结果可能不一样。
这种同一个事务内,同样的读取,在不同的时间段 (依旧还在事务操作中!),读取到了不同的值,这种现象叫做不可重复读。
可重复读
可重读如下所示:

只有当双方的事务都结束后,才能看到最新的数据。
但是,一般的数据库在可重复读情况的时候,无法屏蔽其 他事务insert的数据(为什么?因为隔离性实现是对数据加锁完成的,而insert待插入的数据因为并不存 在,那么一般加锁无法屏蔽这类问题),会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读 情况被读取出来,导致多次查找时,会多查找出来新的记录,就如同产生了幻觉。这种现象,叫做幻读。
串行化
串行化示例如下:

当更新时,数据库的事务被拦住,允许先查的事务先进行。当查的事务结束后,进行更新操作。如下所示 :

一致性
事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务 成功提交的结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中 断,而改未完成的事务对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确(不一 致)的状态。因此一致性是通过原子性来保证的。一致性是要由MySQL和程序员共同来维护的。
MVCC
多版本并发控制MVCC是一种用来解决读-写冲突的无锁并发控制,在MySQL中,每个事物都会有自己的事务ID,可以根据事务ID的大小,来决定事务到来的先后顺序;同时,mysqld可能会面临处理多个事务的情况,事务也有自己的生命周期,mysqld要对多个事务进行管理,管理的方式为"先描述,再组织",事务在MySQL中,一定是对应的一个或者一套结构体对象/类对象,事务也要有自己的结构体。
3个记录隐藏列字段
在MySQL中,有三个隐藏列字段,如下所示:
DB_TRX_ID:6byte,最近修改(修改/插入)事务ID,记录创建这条记录/最后一次修改该记录的事务ID。
DB_ROLL_PTR: 7byte,回滚指针,指向这条记录的上一个版本(简单理解成,指向历史版本,这些数据一般在undolog 中)。
DB_ROW_ID: 6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB 会自动以 DB_ROW_ID 产生一个聚簇索引。
当建立一个标后,MySQL会额外添加以上三列,在更新数据时也会对这三列进行修改。
undo日志
MySQL是以服务进程的方式,在内存中运行,索引、事务、隔离性等都是在内存中完成的,即在MySQL内部的相关缓冲区中,保存相关数据,完成各种判断操作。然后在合适的时候,将相关数据刷新到磁盘当中。
undo日志简单理解就是MySQL中的一段内存缓冲区,用来保存日志数据。
Read View
Read View就是事务进行快照读操作的时候生产的读视图 (Read View),在该事务执行的快照读(读取历史版本)的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)。
Read View在MySQL源码中,就是一个类,本质是用来进行可见性判断的。 即当我们某个事务执行快照读的时候,对该记录创建一个Read View读视图,把它比作条件,用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。
Read View是事务可见性的一个类,不是事务创建出来的就会有Read View,而是当这个事务(已经存在),首次进行快照读的时候,msql会形成Read View。