论文总结与思路分享

LMDB-B+树与MVCC

2020-02-12  本文已影响0人  CPinging

本文承接上一篇https://www.jianshu.com/p/6378082987ec

LMDB中MVCC

当多个用户/进程/线程同时对数据库进行操作时,会出现3种冲突情形:

读-读,不存在任何问题
读-写,有隔离性问题,可能遇到脏读(会读到未提交的数据) ,幻影读等。
写-写,可能丢失更新

通常的读写操作中,我们常使用读写锁来进行对文件的并发操作。文件可支持多个读者,但一旦有写者参与进来,那么文件便需要加锁以便使文件的内容不会混乱。虽然这个解决了一致性的问题,但是却使得并发性很差。于是为了解决并发性问题,MVCC被提出。

MVCC(Multiversion concurrency control)有称为多版本并发控制技术,MVCC是数据库系统实现并发控制和一般系统中实现事务性内存控制的一种技术,主要用于并发控制,使用MVCC将读不会阻塞写,写不会阻塞读,只有两个线程写同一行数据可能导致冲突,因此可以提供最高的并发性。

对于数据库系统来说,若一个用户正在读数据的同时,另一个用户正在写同一个数据,则读用户可能读到写到一半的数据或者不一致的数据,因此数据库系统都会使用并发控制。最简单的方式就是写时阻塞所有读,这就是封锁技术。MVCC的方式是每个用户看到的数据是其连接上数据库时的快照,所有写操作对数据的改变其他数据库用户都不能看到,除非改变已经完成(即事务已提交)。

下面我们使用图来进行一下理解:

image.png

事务T1提交后,事务T2启动,此时事务T3尚未启动,故T2可以看到T1提交后的数据V2。

image.png image.png

当T0和T2都结束,已经没有事务在访问数据V1和V2了,此时V1和V2为dead状态,所以V1和V2都成为VACUUM的处理对象了。

即成为下图中灰色的部分。

image.png

MVCC在LMDB中的优缺点:

优点:

PS:注意,MVCC的写写操作是需要在数据对象上加写锁的,因此对于同一数据对象的写写操作,MVCC也是串行执行的。下文中提到的大量写操作是指多个串行的写操作,由于写中夹杂着读,所以不得不创建非常多的副本给写。但每次只能进行一个写

适合读多、写少的情况。

缺点:

解决方案: LMDB中通过freedb解决,即将不再使用的旧的数据页面空间插入到一颗b-Tree当中,这样旧空间在所有事务不再访问之后就可以被LMDB使用,从而避免了需要定期执行清理操作。

不适合写多的情况。

MVCC引发的一些问题与思考

乐观与悲观锁

数据库MVCC中,2个事务同时修改同一个数据,一个事务检查完没有别的事务对该数据的修改,准备commit。 在检查完和commit的这两个动作之间,第二个事务正好完成commit,此时,如何判断第一个事务能否提交?有办法无锁实现吗?

你可以想简单点,变量x,两个线程试图修改,一个想做x +=2;一个想做x+=1;

1. T1时刻,x=1
2. 线程A读到x=1,经过运算后试图将x改为3,此时A被系统调度走,排在cpu运行队列里面,所以此时A只是试图要把x改为3,实际还没做
3. 线程B起来后也读到了x=1,于是试图将x改为2,并且很幸运的直接改成功了,于是x现在的值是2
4. 线程A再被调度运行的时候,是否能将x直接改为3呢?不可以。因为按照串行之行的要求,无论是先A后B还是反过来,x的值都应该是4。
5. 问题变成A怎么办?这里就有两种基本思路

推广到事物是类似的,如果冲突严重也就是不同线程经常修改同一个变量,那么使用悲观模式,反之,乐观模式。

COW机制与LMDB中的B+树

cow为copy on write,MVCC的基础就是COW,对于不同的用户来说,若其在整个操作过程中不进行任何的数据改变,其就使用同一份数据即可,若需要进行改变,比如增加、删除、修改等,就需要在私有数据版本上进行,修改完成提交之后才给其他事务可见。

mdb_page_touch: 实现COW的技术,复制一个页面,并将更新过B-Tree指针关系的页面插入到B-Tree当中,这样意味着在修改时是在复制的页面上进行修改,别的事务在本事务没有提交之前看到还是以前的数据,提交之后的新事物看到的才是修改之后的数据。

LMDB在存储上使用的是B+树,而该B+树为append类型的。

传统的B+树如下图:

image.png

平面展开图为:

image.png

而append B+树:

以更新leaf 8为例,不能进行原地更新,需要新加入额外的节点(假如是leaf 12),并将数据copoy过去。这样的话,leaf8的所有父亲也需要进行同步的更新,与之同理,所有的父亲的指针无法原地修改,也需要新加入节点,并将数据copy一份。也就是说,会形成如下的B+树图:

image.png

更新操作最终都会转换为B+树的物理存储的append操作,文件不支持内部的修改,只支持append。

为什么要用append的这种形式呢?

因为需要让多个操作可以同时访问,在原来的基础上直接改不是很行。

优缺点如下:

优点:

缺点:

-2 旧数据可以复用,但是I/O成为了随机I/O。效率很低(虽然前面使用了dirty b+树,但是仍然使得这些块是随机的)。

事务的基本特征

原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。

一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。

隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

“重用旧页技术”可以帮助数据库恢复到上一个状态。

上一篇下一篇

猜你喜欢

热点阅读