本博文将描述MVCC和cow技术以及LMDB中如何使用以及实现这两种技术。html
COW(Copy On Write):sql
COW技术背后的思想是拖延技术,基本方法是假若有多个调用者须要访问的资源,在其初始化的时候是不能区分的,即对于多个调用者来讲,这资源就是同样的。这样就能够给每一个数据库
调用者一个指向资源的指针便可。这种方法一直持续到调用者须要进行修改所须要访问的资源时,在这个时候,调用者将被分配到一份真正私有的资源拷贝,这样调用者对资源的任意并发
改动对于其它调用者来讲都是不可见的。全部的以上操做对于调用者来讲是透明的,这种方法最大的好处就是假如调用者不修改资源,私有拷贝就不会被建立。所以这种技术对于读大于函数
写的应用场景来讲特别合适,好比说虚拟内存与分页、数据库、string对象等等,都使用此技术以提高系统性能。高并发
MVCC(Multiversion concurrency control):工具
MVCC是数据库系统实现并发控制和通常系统中实现事务性内存控制的一种技术,主要用于并发控制,使用MVCC将读不会阻塞写,写不会阻塞读,只有两个线程写同一行数据可能致使冲突,post
所以能够提供最高的并发性。MVCC对于并发写同一区域的数据可能致使冲突要求事务回滚或者互相等待资源致使死锁。性能
对于数据库系统来讲,若一个用户正在读数据的同时,另外一个用户正在写同一个数据,则读用户可能读到写到一半的数据或者不一致的数据,所以数据库系统都会使用并发控制。最简单的方式就是优化
写时阻塞全部读,这就是封锁技术。MVCC的方式是每一个用户看到的数据是其链接上数据库时的快照,全部写操做对数据的改变其余数据库用户都不能看到,除非改变已经完成(即事务已提交)。
在MVCC中数据的更新使用delete(标记)+insert实现,所以对于同一行数据可能在数据库多个地方存在不一样版本,旧数据和新数据不在同一个地方(不是就地更新),但只有最后一个版本是最新的,
以前的版本对于以前的事务有效。MVCC的好处是对于读数据来讲,哪怕数据在整个事务过程当中被别的用户修改删除,其读到的数据就是刚开始使用数据库时的那份数据。另外就是其不须要及时删除
旧数据,这样避免了系统来回换页致使性能降低。对于文档型数据库来讲,还能够优化为数据存储在连续区块,delete+insert能够不更新里面部分数据,这样对于后续组装数据提供最大便利。MVCC
提供了即时的一致性视图,读写隔离,不须要进行封锁,所以能够提升并发性。
MVCC的图示:
图1:事务T1改变数据V1,将其改成数据V2,在堆中,数据以下图
图2:事务T3改变了V2,将其改成V3,在堆中,数据以下图:目前事务T2还在活动中,因此V1和V2属于recently dead状态,而不是真的dead状态。
图3:从可视性而言,事务T0只能看到数据V1。由于它早于事务T1启动。
图4:事务T1提交后,事务T2启动,此时事务T3还没有启动,故T2能够看到T1提交后的数据V2。
图5:事务T3提交后,事务T4启动,故T4只能看到数据V3。
图6: 前面说过,当还有事务活动中访问数据V1和V2,V1和V2的状态是recently dead。
当T0和T2都结束,已经没有事务在访问数据V1和V2了,此时V1和V2为dead状态,因此V1和V2都成为VACUUM的处理对象了。
以上几图以postgresql的MVCC实现为例描述了不一样事务间读写操做过程以及其访问的数据。对于同时更新来讲,主要多了
更新冲突检测,若更新存在读写依赖冲突则,更新失败,事务必须回滚,若存在互相依赖,则会解锁某一个事务,以免死锁。
MVCC/COW在LMDB中的实现
LMDB对MVCC加了一个限制,即只容许一个写线程存在,从根源上避免了写写冲突,固然代价就是写入的并发性能降低。由于只有
一个写线程,因此不会不须要wal日志、读写依赖队列、锁队列等一系列控制并发、事务回滚、数据恢复的基础工具。MVCC的基础
就是COW,对于不一样的用户来讲,若其在整个操做过程当中不进行任何的数据改变,其就使用同一份数据便可,若须要进行改变,好比
增长、删除、修改等,就须要在私有数据版本上进行,修改完成提交以后才给其余事务可见。
LMDB中,数据操做的基本单元是页,所以cow也是以页为单位,对应函数是mdb_page_touch,mdb_page_copy,copy真正实现页面复制,
touch调用copy完成复制,而后修改pgno后插入到B+Tree当中,这样对于这次事务,后续的操做访问的数据页就是最新的数据页面,而非
事务启动时对应的数据页面,且此页面与其余页面的关联关系仅在本事务页面列表中可见,对其余事务不可见。
实际上经过以上两个函数也实现了MVCC的核心,对于读写的控制,经过mdb_txn_begin控制,在其中,事务启动时会检查读写锁的状况,
若事务须要更新数据,则会被阻止,若只是读数据,则不论是否有写事务存在,读锁均可以得到。
MVCC的一个反作用就是对于存在大量写的应用,其数据版本不少,所以旧数据会占用大量空间,postgresql解决此问题经过vacuum命令,
LMDB中经过freedb解决,即将再也不使用的旧的数据页面空间插入到一颗b-Tree当中,这样旧空间在全部事务再也不访问以后就能够被LMDB
使用,从而避免了须要按期执行清理操做。固然其反作用是数据只能保持最新不能恢复到任意时刻,未执行vacuum以前,保存全部版本的数据库
能够恢复到任意时刻。
本文参考了以下资料,在此一并表示感谢。
https://en.wikipedia.org/wiki/Multiversion_concurrency_control
http://www.kuqin.com/system-analysis/20120319/319108.html