TheRiver | blog

You have reached the world's edge, none but devils play past here

0%

mysql mvcc

参考

数据库事务、隔离级别和锁

高性能mysql第三版

mysql技术内幕

mysql实战45讲

wikipedia

概念

多版本并发控制(Multiversion concurrency control, MCC 或 MVCC),是数据库管理系统常用的一种并发控制,也用于程序设计语言实现事务内存。[1]

MVCC意图解决读写锁造成的多个、长时间的读操作饿死写操作问题。每个事务读到的数据项都是一个历史快照(snapshot)并依赖于实现的隔离级别。写操作不覆盖已有数据项,而是创建一个新的版本,直至所在操作提交时才变为可见。快照隔离使得事物看到它启动时的数据状态。

特点

  • 主要针对读操作,减少加锁的负担
  • 写操作不覆盖历史版本

innodb的实现

Innodb的mvcc,是通过在每行记录后面保存两个隐藏的列来实现的,增加了一定的存储空间。两个列:

  • 创建事务的trx_id //create_trx_id
  • 删除事物的trx_id //del_trx_id

dml操作如何操纵,利用这两个值(mvcc只对repeatable read/read committed两种隔离级别有效):

select

select取的是snapshot的结果:

更新点:

  • 对于repeatale read,在begin select或者start transaction with consistent snapshot的时候更新快照
  • 对于read committed,在每条语句执行的时候更新快照

快照通过当前事务的trx_id和对应行记录现存的所有版本的create_trx_id,del_trx_id对比得到。不同版本的行记录是通过链表保存的。链表中所有记录的trx_id不是递增的,老得事务可能后提交,链表是根据版本递增的。

如果是rc,在更新点,取已提交的最新版本(< current_trx_id或者 > current_trx_id),每条语句都更新结果.并且del_trx_id为空才可查到
如果是rr,在更新点,取已提交的最新版本(< current_trx_id),当前事务后续进行的修改会更新结果。并且del_trx_id为空才可查到

insert

将create_trx_id更新为当前事务的trx_id

delete

将del_trx_id更新为当前事物的trx_id

update

如果不是主键列,则记录反向的日志,如果执行delete,则undo记录insert,可以反推出更改前的值

如果是主键列,新增一条行记录,也就是多了一个版本,将老得行记录版本的del_trx_id更新为当前事务的trx_id,将新的行记录版本的create_trx_id更新为当前事务的trx_id

对于innodb,mvcc只适用于repeatable read, read committed两种隔离级别,对于read uncommitted,直接读最新版本即可不需要历史版本的参考,而serializable的每次读都是select … lock in share mode,都是加锁读

innodb的purse线程会清除部分历史版本的row记录,当链表中的版本中的create_trx_id小于当前活跃的事务的最小id的时候,这个版本的记录就可能被清楚。

undo log

pgsql中mvcc的实现,是把所有版本都存在b+树中,这样查询很快,删除比较麻烦

innodb通过undo log来保存mvcc的多版本,b+树只保存最新提交的版本,老版本保存在undolog中,通过链表串起来

undo log也会持久化的,默认存在共享表空间

undo log也可用于事务回滚,保证事务的一致性,undo log记录的是逻辑日志。

mvcc的作用

如果有人这样问,总得有个差不多的听起来是对的回答:

降低多版本并发时候的读负担(相对加锁),以牺牲一点存储空间为代价

ending

----------- ending -----------