MVCC是Multi Version Concurrency Control的简称,代表多版本并发控制
。为什么需要MVCC,还要从数据库事务的ACID特性说起。
MVCC 可以保证不阻塞地读到一致的数据。但是,MVCC 并没有对实现细节做约束,为此不同的数据库的语义有所不同,比如:
postgres 对写操作
也是乐观并发控制
;在表中保存同一行数据记录的多个不同版本,每次写操作,都是创建,而回避更新;在事务提交时,按版本号检查当前事务提交的数据是否存在写冲突,则抛异常告知用户,回滚事务;
innodb 则只对读无锁,写操作
仍是上锁的悲观并发控制
,这也意味着,innodb 中只能见到因死锁和不变性约束而回滚,而见不到因为写冲突而回滚;不像 postgres 那样对数据修改在表中创建新纪录,而是每行数据只在表中保留一份,在更新数据时上行锁,同时将旧版数据写入 undo log;表和 undo log 中行数据都记录着事务ID
,在检索时,只读取
来自当前已提交的事务的行数据
;
可见 MVCC 中的写操作仍可以按悲观并发控制实现,而 CAS 的写操作只能是乐观并发控制。
还有一个不同在于,MVCC 在语境中倾向于 “对多行数据打快照造平行宇宙”,然而 CAS 一般只是保护单行数据而已。比如 mongodb 有 CAS 的支持,但不能说这是 MVCC。
InnoDB与MVCC
MySQL中的InnoDB存储引擎的特性有,默认隔离级别REPEATABLE READ, 行级锁,实现了MVCC, Consistent nonlocking read(默认读不加锁,一致性非锁定读), Insert Buffer, Adaptive Hash Index, DoubleWrite, Cluster Index。
上面列举了这么多,表示InnoDB有很多特性、很快。
InnoDB中通过UndoLog实现了数据的多版本
,而并发控制通过锁来实现。
Undo Log除了实现MVCC外,还用于事务的回滚。
Redo log, bin log, Undo log
MySQL Innodb中存在多种日志,除了错误日志、查询日志外,还有很多和数据持久性、一致性有关的日志。
binlog,是mysql服务层产生的日志,常用来进行数据恢复、数据库复制,常见的mysql主从架构,就是采用slave同步master的binlog实现的, 另外通过解析binlog能够实现mysql到其他数据源(如ElasticSearch)的数据复制。
redo log记录了数据操作在物理层面的修改,mysql中使用了大量缓存,缓存存在于内存中,修改操作时会直接修改内存,而不是立刻修改磁盘,当内存和磁盘的数据不一致时,称内存中的数据为脏页(dirty page)。为了保证数据的安全性,事务进行中时会不断的产生redo log,在事务提交时进行一次flush操作,保存到磁盘中, redo log是按照顺序写入的,磁盘的顺序读写的速度远大于随机读写。当数据库或主机失效重启时,会根据redo log进行数据的恢复,如果redo log中有事务提交,则进行事务提交修改数据。这样实现了事务的原子性、一致性和持久性。
Undo Log: 除了记录redo log外,当进行数据修改时还会记录undo log,undo log用于数据的撤回操作,它记录了修改的反向操作,比如,插入对应删除,修改对应修改为原来的数据,通过undo log可以实现事务回滚,并且可以根据undo log回溯到某个特定的版本的数据,实现MVCC。
redo log 和binlog的一致性,为了防止写完binlog但是redo log的事务还没提交导致的不一致,innodb 使用了两阶段提交
大致执行序列为
InnoDB prepare (持有prepare_commit_mutex);
write/sync Binlog;
InnoDB commit (写入COMMIT标记后释放prepare_commit_mutex)。
MVCC实现
innodb中通过B+树作为索引的数据结构,并且主键所在的索引为ClusterIndex(聚簇索引), ClusterIndex中的叶子节点中保存了对应的数据内容。一个表只能有一个主键,所以只能有一个聚簇索引,如果表没有定义主键,则选择第一个非NULL唯一索引作为聚簇索引,如果还没有则生成一个隐藏id列作为聚簇索引。
除了Cluster Index外的索引是Secondary Index(辅助索引)。辅助索引中的叶子节点保存的是聚簇索引的叶子节点的值。
InnoDB行记录中除了刚才提到的rowid外,还有trx_id和db_roll_ptr, trx_id表示最近修改的事务的id
,db_roll_ptr指向undo segment中的undo log。
新增一个事务时事务id会增加,trx_id能够表示事务开始的先后顺序。
undo log
分为Insert和Update两种,delete可以看做是一种特殊的update,即在记录上修改删除标记。
update undo log记录了数据之前的数据信息,通过这些信息可以还原到之前版本的状态。
当进行插入操作时,生成的Insert undo log在事务提交后即可删除,因为其他事务不需要这个undo log
。
进行删除修改操作时,会生成对应的undo log,并将当前数据记录中的db_roll_ptr指向新的undo log
转载:https://www.zhihu.com/question/27876575/answer/62496641
https://liuzhengyang.github.io/2017/04/18/innodb-mvcc/
https://www.cnblogs.com/xinysu/p/6555082.html