-
什么是mvcc
Mysql的隔离级别是‘可重复读’,即:事务A在读到一条数据之后,此时事务B对该数据进行了修改操作并提交,那么事务A再读该数据,依然还是原来的内容。它的实现原理是MVCC(Multi-Version Concurrency Control)多版本并发控制,MVCC保证当前查询为快照读,所以不受其他事务影响。
什么是当前读和快照读
- 当前读:读取当前版本的数据即最新的数据,并且会对读取的数据加锁,防止被其他事务修改。
- 快照读:事务A查询数据时把查询到的数据拍成一张照片,事务A再次查询数据时好像一直查询的那张照片上的数据,但是数据会被事务B、事务C修改,所以查询的不一定是正确数据。
‘可重复读’可以预防那些事务问题
- 未提交时读 Read Uncommitted(最低级别,任何情况都无法保证。)
- 已提交后读 Read Committed(可避免脏读的发生。)
- 可重复读 Repeatable Read(可避免脏读、不可重复读的发生。)
- 序列化(串行化) Serializable(可避免脏读、不可重复读、幻读的发生。)
mvcc如何实现RC和RR的隔离级别
- RC(Read Committed)的隔离级别下,每个快照读都会生成并获取最新的readview。
- RR(Repeatable Read)的隔离级别下,只有在同一个事务的第一个快照读才会创建readview,之后的每次快照读都使用的同一个readview,所以每次的查询结果都是一样的。
mvcc的原理
mvcc实现原理主要有三个部分:隐藏字段、undoLog、ReadView。
1、隐藏字段
- DB_ROW_ID : 隐藏ID,当创建表没有合适的索引作为聚集索引时,会用该隐藏ID创建聚集索引;
- DB_TRX_ID : 记录操作该数据事务的事务ID;
- DB_ROLL_PTR : 指向上一个版本数据在undo log 里的位置指针;
2、undoLog(Mysql三大日志之一)
-
Undo Log:事务回滚
数据库事务四大特性中有一个是原子性,具体来说就是原子性是指对数据库的一系列操作,要么全部成功,要么全部失败,不可能出现部分成功的情况。实际上,原子性底层就是通过undo log实现的。undo log主要记录了数据的逻辑变化,比如一条 INSERT 语句,对应一条 DELETE 的undo log,对于每个 UPDATE 语句,对应一条相反的 UPDATE 的undo log,这样在发生错误时,就能回滚到事务之前的数据状态。这些多条数据的更新成为版本链(在每次更新该记录后,都会将旧值放到一条undo日志中。随着更新次数的增多,所有的版本都会被roll_pointer属性连接成一条链表,这个链表就称之为版本链)。
-
Bin Log:数据同步
binlog用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。binlog是mysql的逻辑日志,并且由Server层进行记录,使用任何存储引擎的mysql数据库都会记录binlog日志。binlog是通过追加的方式进行写入的,可以通过max_binlog_size参数设置每个binlog文件的大小,当文件大小达到给定值之后,会生成新的文件来保存日志。
-
Redo Log:数据备份
redo log包括两部分:一个是内存中的日志缓冲(redo log buffer),另一个是磁盘上的日志文件(redo log file)。mysql每执行一条DML语句,先将记录写入redo log buffer,后续某个时间点再一次性将多个操作记录写到redo log file。这种先写日志,再写磁盘的技术就是MySQL里经常说到的WAL(Write-Ahead Logging) 技术。
3、ReadView
Read View就是一个保存事务ID的list列表,这个列表用来记录当前数据库系统中活跃的读写事务,也就是已经开启了,正在进行数据操作但是还未提交保存的事务。可以通过这个列表来判断某一个版本是否对当前事务可见。其中,有四个重要的字段:
- creator_trx_id:创建当前Read View所对应的事务ID
- m_ids:所有当前未提交事务的事务ID,也就是活跃事务的事务id列表
- min_trx_id:m_ids里最小的事务id值
- max_trx_id:InnoDB 需要分配给下一个事务的事务ID值(事务 ID 是累计递增分配的,所以后面分配的事务ID一定会比前面的大!)
如何通过ReadView来判断记录的某个版本是可见的?(小于、等于、不在、坚持回溯)
(trx_id表示要读取的事务id)
- 如果trx_id == creator_trx_id,则表明当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
- 如果trx_id < min_trx_id,则表明生成该版本的事务在当前事务生成ReadView之前已经提交了,所以该版本可以被当前事务访问。
- 如果trx_id >= max_trx_id,则表明生成该版本的事务在当前事务生成ReadView之后才开启,所以该版本不可以被当前事务访问。
- 如果trx_id in m_ids,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。
- 如果trx_id not in m_ids,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
- 如果某个版本的数据对当前事务不可见,那就顺着版本链找到下一个版本的数据,并继续执行上面的步骤来判断记录的可见性,以此类推,直到版本链中的最后一个版本。