mysql事物原理(一)-undo log、redo log、MVCC

redo log

redo log叫做重做日志.用于解决数据库事物提交 还未刷入磁盘,服务器down机导致的数据丢失的问题。

InnoDB作为MySQL的存储引擎,数据存储在磁盘中,如果每次读写数据都要操作磁盘IO效率会很低,为此InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(刷脏)

Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。

于是,redo log被引入来解决这个问题:当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。

既然redo log也需要在事务提交时将日志写入磁盘,为什么它比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快呢?主要有以下两方面的原因:

(1)刷脏是随机IO,因为每次修改的数据位置随机,但写redo log是追加操作,属于顺序IO。

(2)刷脏是以数据页(Page)为单位的,MySQL默认页大小是16KB,一个Page上一个小修改都要整页写入;而redo log中只包含真正需要写入的部分,无效IO大大减少。

undo log

undo log的2个主要作用是实现MVCC和事物回滚

当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,反之亦然,当 update 一条记录时,它记录一条对应相反的 update 记录,如果 update 的是主键,则是对先删除后插入的两个事件的反向逻辑操作的记录。

 在事物回滚时就会反向读取相应内容进行回滚,也可以根据undo log读取到被修改后数据的原值

MVCC的实现原理

MVCC的实现主要利用到了数据库隐式字段和undo log、ReadView

MVCC的好处主要实现思想是通过数据多版本来做到读写分离。从而实现不加锁读进而做到读写并行。

MVCC结构

Column1 Column2位数据格式

DB_TRX_ID(隐式字段) 最近一次修改的事物id(注:事物id都是递增的)

DB_ROLL_PTR(隐式字段)  回滚日志 指向undo log

DB_ROW_ID(隐式字段) 自增ID如果数据库表没有指定主键 则默认以此作为聚簇索引

undo log 版本链

事物A id=1的事物将小明改为小张

事物B id=2的事物将age改为30

ReadView

read view(快照)是基于undo log实现的可以实现不加锁的情况下并发读 读已提交和可重复读都是通过ReadView实现

快照读和当前读

详情可可以看《快照读和当前读》

数据结构

m_ids:当前有哪些事务正在执行,且还没有提交,这些事务的 id 就会存在这里

min_trx_id:是指 m_ids 里最小的值

max_trx_id:下一个要生成的事物id,因为事物id是递增的 肯定比当前所有事物id要大

creator_trx_id: 创建read view的事物id

RE隔离级别读已提交实现原理

图1.事物A id等于1将数据修改并提交

图2.事物B执行查询

 

1.事物B执行快照读(注:RR隔离级别只会生成一次快照 RE隔离级别每次都会生成新的快照

2.读取到数据通过数据隐藏列DB_TRX_ID值1跟min_trx_id做比较, DB_TRX_ID<min_trx_id 说明最近修改此数据的事物已经提交则可以正常读取 column1=小张   column2=32

图3

1.事物C将colunm2=32 改为16事物并未提交

2.事物B执行select读取数据通过DB_TRX_ID与min_trx_id做比较 发现比最小事物id大。表示有可能读到的。再将DB_TRX_ID=4在min_ids进行查找 成功找到。发现事物还未提交

3.通过undolog链跟往上找(通步骤2一样的查找方式)成功查到 colunm1=小明 colunm2=32的数据 成功读取  

图4

1.又回到了图一。事物C提交事物后。事物B进行查询重新生成快照(RE隔离级别每次都会生成新的快照) 

2.这个时候m_ids里面已经没有4了。数据行DB_TRX_ID>min_trx_id 同时又不在m_ids里面存在表示已经提交了直接读取

RR可重复读实现原理

流程都一样 唯一不同的是只会生成一次快照,就算事物C提交了  m_ids[2,4] 还是有2和4 所以读到的数据还是小明 32

原文地址:https://www.cnblogs.com/LQBlog/p/15129211.html