事务的特性(ACID)
事务要求 ACID 的特性,即:原子性、一致性、隔离性、持久性 。
-
原子性:是指整个数据库的每个事务都是不可分割的单位。只有事务中的所有 SQL 语句都执行成功,才算整个事务成功,事务才会被提交。如果事务中任何一个 SQL 语句执行失败,整个事务都应该被回滚。 undolog 来保证 -
一致性:是指将数据库从一种一致性状态转换为下一种一致性状态。不允许数据库中的数据出现新老数据都有的情况,要么都是老数据,要么都是新数据。用更书面化的表达就是:数据的完整性约束没有被破坏。 -
隔离性:是指一个事务的影响在该事务提交前对其他事务都不可见,它通过锁机制来实现。 mvcc 和 锁 来保证 -
持久性:是指事务一旦被提交,其结果就是永久性的。即使发生宕机等故障,数据库也能将数据恢复。 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;
隔离级别的一致性的关系
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|
读未提交 | 可能 | 可能 | 可能 | 读已提交 | 不可能 | 可能 | 可能 | 可重复读 | 不可能 | 不可能 | 对innodb不可能 | 串行化 | 不可能 | 不可能 | 不可能 |
MVCC
Multi Version Concurrency Control 翻译为中文即 多版本并发控制。 用来实现不加锁情况下的读一致性和隔离性。 MVCC使得InnoDB的事务隔离级别下执行一致性读操作有了保证,换言之,就是为了查询一些正在被另一个事务更新的行, 并且可以看到它们被更新之前的值。这是一个可以用来增强并发性的强大的技术,因为这样一来的话查询就不用等待另一个事务释放锁。这项技术在数据库领域并不是普遍使用的。一些其它的数据库产品, 以及mysql其它的存储引擎并不支持它。
mvcc的实现,基于undolog 、版本链 、readview
-
在 MySQL 命令行的默认设置下,事务是自动提交的,即执行了SQL 语句之后会马上执行 commit 操作,我们可以设置 set autocommit=0 来禁用当前回话的自动提交。 -
还可以用 begin 、start transaction 来显式的开始一个事务。 -
commit 在默认设置下是等价于 commit work 的,表示提交事务。 -
rollback 在默认设置下等价于 rollback work,表示事务回滚。 -
savepoint xxx 表示定义一个保存点,在一个事务中可以有多个保存点。 -
release savepoint xxx 表示删除一个保存点,当没有该保存点的时候执行该语句,会抛出一个异常。 -
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}
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是多少。
- trx_id < up_limit_id || trx_id == creator_trx_id(显示) 说明该行的记录早就提交了事务,当前的事务是可见的。 或者该行的最后修改就是本事务提交的,也可见。
- trx_id >= low_limit_id(不显示) 读视图(read view) 如果数据事务ID(trx_id)大于read view 中的当前系统的最大事务ID,则说明该数据是在当前read view 创建之后才产生的,所以数据不显示。
- 判断 trx_id 是否在活跃事务( trx_ids )中
- 不存在 :则说明read view产生的时候事务 已经commit 了,这种情况数据则可以
显示 。 - 已存在 :则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据, 我当前事务也是
看不见 的。
mvcc如何实现RC和RR的隔离级别
(1)RC的隔离级别下,每个快照读都会生成并获取最新的readview。
(2)RR的隔离级别下,只有在同一个事务的第一个快照读才会创建readview,之后的每次快照读 都使用的同一个readview,所以每次的查询结果都是一样的。
|