MySQL InnoDB事务讲解
1. 数据库事务的ACID特性:A(atomicity,原子性)、C(consistency,一致性)、I(isolation,隔离性)、D(durability,持久性)
2. 事务隔离级别:查看事务隔离级别命令,show variables like '%isolation%';或select @@transaction_isolation;
读未提交(RU/read uncommitted):一个事务读取到另一个事务未提交的数据,造成脏读;
读已提交(RC/read committed):一个事务读取到另一个事务已提交的数据,导致同一条记录读取两次以上的结果不一致,造成不可重复读;
可重复读(RR/repeatable):一个事务在读取到数据后,如果其他事务修改了相同记录的数据并提交,再次读取的数据仍为第一次读取到的记录一样;而对于insert或delete操作,会读取到另一个事务提交后的数据,导致两次读取结果不一样,这就是幻读;
串行化(serializable):所有事务串行执行,不会出现脏读、不可重复读、幻读问题,但性能最差
3. 数据库解决并发问题的两种方案:LBCC(Lock Based Concurrency Control,基于锁的并发控制)和MVCC(Multi Version Concurrency Control,多版本并发控制),其中LBCC性能低下,MySQL InnoDB采用的是MVCC,普通的select读取数据不会加锁,提高了数据库并发处理能力,而MVCC只对RR和RC两个事务隔离级别起作用。
4. InnoDB MVCC机制:数据操作过程中生成的多版本记录行,会通过purge线程进行清理
1)、对每行数据的修改操作都会生成一个副本记录行,每个记录行上都有隐藏的两个字段,事务ID和行的回滚指针;开始新事务时,都会将当前事务ID放到影响数据行记录的事务ID字段上,注意事务ID是递增且不重复的,当查询时,会用当前事务ID和每行记录的事务ID进行比较;行的回滚指针则指向上一个版本记录行。
select:借助readview和undo log,遍历每个版本的记录行找到要查询的数据
insert: 新插入一行,且保存当前事务编号作为版本号
delete:生成删除行记录,且将当前事务编号作为行删除标识
update:新插入一行,且保存当前事务编号作为版本号,同时将当前事务编号保存到原来行记录作为删除标识
2)、undo log:InnoDB为了更好支持并发,多版本一致性读(当前读和快照读)采用基于回滚段(副本记录行)的方式。如果是Insert操作,会产生insert undo log,由于insert操作只对当前事务可见,其他事务不可见,所以insert undo log在事务提交后直接删除,不需要purge操作;如果是delete和update操作,会产生update undo log,由于update undo log会用于后续的MVCC中,也就是其他事务可能会用到这些记录,所以事务提交后不能立即删除,而是放到undo log链表(多个副本记录行)中,等待purge线程清理。
3)、read view:包含了当前系统中所有活跃的读写事务ID,放在一个名为m_ids的列表中。在RR或RC隔离级别下,用来判断undo log版本链中的哪个版本是当前事务可见的。RR隔离级别下,同一个事务中,只在第一次查询时生成read view;RC隔离级别下,同一个事务中,会在每次查询时生成最新的readview。判断版本可见步骤如下:
- 被访问版本的事务ID如果小于m_ids列表中最小的事务ID,表明该版本的事务已经在生成readview前提交,所以该版本可以被当前事务访问;
- 被访问版本的事务ID如果大于m_ids列表中最大的事务ID,表明该版本的事务在生成readview后才生成,所以该版本不可以被当前事务访问;
- 被访问版本的事务ID如果在m_ids列表中最大值和最小值之间,那就要判断该版本的事务ID是否在m_ids列表中,如果在,则表示事务还是活跃的,不可被访问,如果不在,表示事务已经提交,可以被访问。
5. MVCC下的读操作分为快照读和当前读
1)、快照读(一致性非锁定读):如简单的select(非加锁的select)操作,读取的是历史版本,不会对记录加锁;
2)、当前读:如insert、update、delete操作,需要加锁,属于当前读,每次操作时会对行加锁,读取当前版本数据,除insert、update、delete外还有select for update等加X锁(排它锁)操作语句
6. MVCC下事务回滚操作:就是将对应事务ID的所有版本undo log记录删除,就类似链表中删除节点。
7. MVCC下启动事务,数据写入时,会先将undo log的操作写入redo log,如果undo log操作没有成功写入redo log,表示事务没有提交且没有undo log操作记录,系统奔溃,当系统恢复时无需操作;如果undo log操作写入redo log成功且落盘,而buffer pool中脏页落盘失败,也就是没有成功落盘到redo log文件,没有提交事务,系统奔溃,当系统恢复时,需要根据redo log将未提交的事务进行回滚。
8. 注意:undo log是在系统表空间中,可以看上一篇的系统表空间图,redo log记录的是操作,便于系统数据恢复。
9. 显示事务需要通过begin开启,而没有通过命令开启事务的SQL,就会有隐式事务,当执行前默认开启,执行结束则提交。