参考
概念
多版本并发控制(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的作用
如果有人这样问,总得有个差不多的听起来是对的回答:
降低多版本并发时候的读负担(相对加锁),以牺牲一点存储空间为代价