事务的特性(ACID)
事务要求 ACID 的特性,即:原子性、一致性、隔离性、持久性 。
1.原子性:是指整个数据库的每个事务都是不可分割的单位。只有事务中的所有 SQL 语句都执行成功,才算整个事务成功,事务才会被提交。如果事务中任何一个 SQL 语句执行失败,整个事务都应该被回滚。 undolog 来保证
2.一致性:是指将数据库从一种一致性状态转换为下一种一致性状态。不允许数据库中的数据出现新老数据都有的情况,要么都是老数据,要么都是新数据。用更书面化的表达就是:数据的完整性约束没有被破坏。
3.隔离性:是指一个事务的影响在该事务提交前对其他事务都不可见,它通过锁机制来实现。 mvcc 和 锁 来保证
4.持久性:是指事务一旦被提交,其结果就是永久性的。即使发生宕机等故障,数据库也能将数据恢复。 redolog 来保证
事务隔离级别
首先介绍几个概念:
1、 脏读 :事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、 不可重复读 :事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交导致事务A多次读取同一数据时,结果不一致。
3、 幻读 :系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在 这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生 了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆, 不可重复读 侧重于修改, 幻读 侧重于新增或删除。
读未提交 (read uncommitted)
在该级别,所有的事务都可以看到其他未提交事务的执行结果,本隔离级别很少用于实际应用,因为它的 性能不比其他级别好多少。读取未提交的数据,也称之为脏读。
set global transaction isolation level read uncommitted; #查看当前隔离级别 select @@global.tx_isolation,@@tx_isolation;
读已提交 (read committed)
事务A只能读取到事务B提交的数据,这种级别可以避免“脏数据” ,这种隔离级别会导致“不可重复读取” ,Oracle默认隔离级别
set global transaction isolation level read committed; 查看当前隔离级别 select @@global.tx_isolation,@@tx_isolation;
可重复读(repeatable read)
是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的 结果⼀致。但也会有新的问题,⽐如级别的事务正在执⾏时,另⼀个事务成功的插⼊了某条数 据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败 因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读
set global transaction isolation level repeatable read; 查看当前隔离级别 select @@global.tx_isolation,@@tx_isolation;
串行化(SERIALIZABLE)
事务最高隔离级别,它会强制事务排序,使之不会发⽣冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使⽤的场景并不多。
set global transaction isolation level serializable; 查看当前隔离级别 select @@global.tx_isolation,@@tx_isolation;
隔离级别的一致性的关系
MVCC
Multi Version Concurrency Control
翻译为中文即 多版本并发控制。 用来实现不加锁情况下的读一致性和隔离性。 MVCC使得InnoDB的事务隔离级别下执行一致性读操作有了保证,换言之,就是为了查询一些正在被另一个事务更新的行, 并且可以看到它们被更新之前的值。这是一个可以用来增强并发性的强大的技术,因为这样一来的话查询就不用等待另一个事务释放锁。这项技术在数据库领域并不是普遍使用的。一些其它的数据库产品, 以及mysql其它的存储引擎并不支持它。
mvcc的实现,基于undolog
、版本链
、readview
1.在 MySQL 命令行的默认设置下,事务是自动提交的,即执行了SQL 语句之后会马上执行 commit 操作,我们可以设置 set autocommit=0 来禁用当前回话的自动提交。
2.还可以用 begin 、start transaction 来显式的开始一个事务。
3.commit 在默认设置下是等价于 commit work 的,表示提交事务。
4.rollback 在默认设置下等价于 rollback work,表示事务回滚。
5.savepoint xxx 表示定义一个保存点,在一个事务中可以有多个保存点。
6.release savepoint xxx 表示删除一个保存点,当没有该保存点的时候执行该语句,会抛出一个异常。
7.rollback to [savepoint] xxx 表示回滚到某个保存点。
--查询事务自动提交状态 show variables like '%commit%';
版本链
每行数据都有隐式字段
DB_TRX_ID 最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_ROLL_PTR 7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
DB_ROW_ID 6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以 DB_ROW_ID产生一个聚簇索引
实际还有一个删除flag隐藏字段, 既记录被更新或删除并不代表真的删除,而是删除flag变了
读视图(read view)
事务id是递增的只会越来越大,在开启事务的时候,第一次快照读会产生一个读视图,将目前活跃的还未提交的事 务记录下来,并排序出最低事务和最高事务。
我们可以将Read View看作一个数组,整个数组的左边界和右边界时当前活跃事务的事务Id。举个例子 :
现在存活事务有事务100,150,200,250
那么Read View就是{100,150,200,250}
//查询事务Id的语句 SELECT tx.trx_id FROM information_schema.innodb_trx tx WHERE tx.trx_mysql_thread_id = connection_id()
Read View几个属性
trx_ids
:当前系统活跃(未提交)事务版本号集合。
low_limit_id
: 创建当前read view 时“当前系统最大事务版本号+1”。
up_limit_id
: 创建当前read view 时“系统正处于活跃事务最小版本号”
creator_trx_id
: 创建当前read view的事务版本号;
在每次查询数据的时候,会判断当前行数据最新的事务id trx_id是多少。
1.trx_id < up_limit_id || trx_id == creator_trx_id(显示) 说明该行的记录早就提交了事务,当前的事务是可见的。 或者该行的最后修改就是本事务提交的,也可见。
2.trx_id >= low_limit_id(不显示) 读视图(read view) 如果数据事务ID(trx_id)大于read view 中的当前系统的最大事务ID,则说明该数据是在当前read view 创建之后才产生的,所以数据不显示。
3.判断 trx_id 是否在活跃事务( trx_ids )中
(1)不存在 :则说明read view产生的时候事务 已经commit了,这种情况数据则可以显示 。
(2)已存在 :则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据, 我当前事务也是看不见的。
mvcc如何实现RC和RR的隔离级别
(1)RC的隔离级别下,每个快照读都会生成并获取最新的readview。
(2)RR的隔离级别下,只有在同一个事务的第一个快照读才会创建readview,之后的每次快照读都使用的同一个readview,所以每次的查询结果都是一样的。