数据库InnoDB-MVCC-多版本并发控制
数据库InnoDB-MVCC-多版本并发控制介绍;MVCC的具体实现有三个隐藏字段DB_TRX_ID(最近修改事务ID),DB_ROLL_PTR回滚指针,不同事务或者相同事务对同一数据进行修改是,会形成undo log版本链,以便在读取的时候回滚。在RC隔离级别,为了保证读已提交,则每次读都会生成ReadView,然后与版本链中的记录进行对比,选择符合版本链规则的数据进行读。......
MVCC
MVCC 称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undolog日志、readView。
当前读与快照读
当前读读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
快照读:简单的select
(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
Read Committed
:每次select,都生成一个快照读。Repeatable Read
:开启事务后第一个select语句才是快照读的地方。Serializable
:快照读会退化为当前读。
三个隐式字段
当我们创建一张表的时候,表中除了会又创建时添加的字段外,InnoDB
还会自动的给我们添加三个隐藏字段及其含义分别是:
隐藏字段 | 含义 |
---|---|
DB_TRX_ID | 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。 |
DB_ROLL_PTR | 回滚指针,指向这条记录的上一个版本,用于配合undolog,指向上一个版本。 |
DB_ROW_ID | 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。 |
上述的前两个字段是肯定会添加的,是否添加最后一个字段DB_ROW_ID
,得看当前表有没有主键,如果有主键,则不会添加该隐藏字段。
Undo log与undo log版本链
undo log
回滚日志,在insert
、update
、delete
的时候产生的便于数据回滚的日志。当insert
的时候,产生的undo log
日志只在回滚时需要,在事务提交后,可被立即删除。而update
、delete
的时候,产生的undo log
日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。
版本链
有一张原始的数据表为
id | age | name | DB_TRX_ID | DB_ROLL_PTR |
---|---|---|---|---|
2 | 30 | A30 | 1 | null |
其中:
DB_TRX_ID
:代表最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID,是
自增的。
DB_ROLL_PTR
:由于这条数据是才插入的,没有被更新过,所以该字段值为null。
有四个并发事务同时访问这张表:
当执行完事务1,2,3中的对同一个数据的更改操作后,undo log会形成undo log版本链,记录数据变更之前的样子;然后更新记录,并且记录本次操作的事务ID,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本。
不同事务或相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
Readview
ReadView
(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
ReadView
中包含了四个核心字段:
字段 | 含义 |
---|---|
m_ids | 当前活跃的事务ID集合 |
min_trx_id | 最小活跃事务ID |
max_trx_id | 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的) |
creator_trx_id | ReadView创建者的事务ID |
而在readview中就规定了版本链数据的访问规则:
trx_id
代表当前undolog版本链对应事务ID。
条件 | 是否可以访问 | 说明 |
---|---|---|
trx_id ==creator_trx_id | 可以访问该版本 | 成立,说明数据是当前这个事务更改的。 |
trx_id < min_trx_id | 可以访问该版本 | 成立,说明数据已经提交了。 |
trx_id > max_trx_id | 不可以访问该版本 | 成立,说明该事务是在ReadView生成后才开启。 |
min_trx_id <= trx_id<= max_trx_id | 如果trx_id不在m_ids中,是可以访问该版本的 | 成立,说明数据已经提交。 |
不同的隔离级别,生成ReadView的时机不同:
READ COMMITTED
:在事务中每一次执行快照读时生成ReadView。REPEATABLE READ
:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
在READ COMMITTED
中,每执行一次select语句就会执行快照读生成ReadView
,然后数据库就会根据所生成的ReadView
以及ReadView
的版本链访问规则,到undo log
版本链中匹配数据,最终决定此次快照读返回的数据。
RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。而RR是可重复读,在一个事务中,执行两次相同的select语句,查询到的结果是一样的。
MVCC与锁机制保证了数据库的隔离性。数据事务的其他性质及保证可以参考:
Mysql事务详解-[数据库的隔离级别、脏读、不可重复读、幻读以及ACID性质与redo log与undo log]
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)