MySQL 事务与MVCC

MySQL事务隔离级别

隔离程度从弱到强依次是:读未提交(read uncommitted) < 读已提交(read committed) < 可重复读(repeatable read) < 串行读(serializable read)

更强的隔离级别能在低级别的隔离级别基础上,提供更强的隔离保证。而不是说这种隔离级别提供这种能力,那种隔离级别提供与之相对的能力。

SQL标准中定义,可重复读隔离级别,幻读问题仍可能存在。但是在InnoDB引擎Repeatable Read级别中,通过next key locking解决了幻读问题。

幻读(Phantom read)

定义:幻读是指一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

幻读说明:1)幻读特指看到了新增的行,如果是看到了更新之后的数据不叫幻读;2)在可重复读的事务隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的,因此幻读只在当前读下才会发生;

幻读带来的问题:1)可重复读的语义被破坏了;2)数据一致性会有问题,也就是数据库中的数据和基于该数据库操作产生的binlog重放在备份库的数据会和原主库不一致;

幻读产生的原因:在并发操作时,数据库中的行锁只能锁住行,但是新插入记录这个动作是要更新记录之间的“间隙”;

间隙锁(Gap lock)

为了解决幻读问题,InnoDB引入了间隙锁(Gap lock),顾名思义,就是锁的两个值之间的间隙。

InnoDB自动使用间隙锁的条件:1)必须在Repeatable Read隔离级别下;2)检索条件必须有索引(没有索引的话,MySQL会全表扫描,那样会锁定整张表所有记录,包括不存在的记录,此时其它事务不能修改不能删除不能添加)

多版本并发控制(MVCC)

多版本并发控制是一种技术概念,并没有统一的实现标准,其核心理念是不同的事务访问不同版本的数据快照,从而实现不同的事务隔离级别。当用户读取某条记录被其它事务占用时,当前事务可以通过Undo日志读取到该行之前的数据,以此实现非锁定读取。

MVCC只在Repeatable Read 和Read Committed两个事务隔离级别下工作。因为Read Uncommitted 总是读取数据行的最新值,而Serializable 则会对读取的数据行加锁。

从本质上讲,Read Committed隔离级别违背了事务原则中的“隔离性(I)”,因此是一种隔离程度比较弱的事务隔离级别。

Read Committed与Repeatable Read 的区别是:在开启事务后进行快照读时,Read Committed能读到最新提交的事务所做的修改;而Repeatable Read 只能读到在事务开启后第一次SELECT读操作之前提交的修改,因此是可重复读;

MVCC是基于InnoDB的Undo log实现的

实现原理

SELECT:查询时,将当前事务版本号和记录的版本进行比对:SELECT create_no <= current_no AND expired_no is null or expired_no >= current_no。

INSERT: InnoDB为新插入的每一行保存当前系统版本号作为行版本号;

Redo Log

用于保证事务的持久性。InnoDB中通过Force Log at commit机制实现事务的持久性,即当事务提交时,必须将该事务的所有日志写入到redo日志文件进行持久化。

redo 日志基本上是顺序写,在数据库运行时不需要对redo log的文件进行读取操作。而undo log则需要随机读写。

为了确保每次操作日志都写入redo log文件,在每次将redo log缓冲写入redo log文件后,innodb引擎都需要调用一次fsync操作。

Undo Log

用于事务回滚和实现MVCC

回滚:如果用户执行的事务或者语句由于某种原因失败了,又或者用户用ROLLBACK语句请求回滚,就可以利用Undo log将数据回滚到修改之前。

Undo所做的恢复并非物理恢复,仅仅将数据库逻辑恢复到原来,数据结构和页本身回滚后可能不同。

MVCC:不同事务或者相同事务对同一记录行的修改,会使该记录行的undo log成为一条链表,undo log的链首就是最新的记录,尾部就是最早的记录。当一个事务读取记录行时,如果当前的记录行不可见,可以顺着undo log链找到满足其可见性条件的记录行版本。

Undo log分类:1)insert undo log:事务对insert新纪录时产生的undo log,只在事务回滚时需要,并且在事务提交后就可以立即丢弃;2)update undo log:事务对记录进行delete和update操作时产生的undo log,不仅在事务回滚时需要,RR级别下的快照读也需要,只有当数据库所使用的快照中不涉及该日志记录时,对应的redo log 才会被purge线程删除。

Purge线程

为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下旧记录的deleted_bit,并不真正将就记录删除。为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。purge线程自己也维护了一个read view,如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read_view可见,那么这条记录一定是可以被安全清楚。

Read view与可见性比较算法

Read Committed与Repeatable Read隔离级别是利用 consistent read view(一致性视图)方式支持的,一致性视图就是在某个时刻给事务系统trx_sys打snapshot(快照),把当时的trx_sys状态(包括活跃读写事务数组)记下来,之后所有的读操作根据其事务ID(即trx_id)与snapshot中的trx_sys

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java