Python/Go开发

PostgreSQL并发处理方式——MVCC

2017-01-09  本文已影响0人  ZYJ2016

PostgreSQL的特色之一是它的并发控制机制,在维护一致性和完整性的同时,尽量避免读写的堵塞。

对于传统数据库,为了维护一致性和完整性,避免一个事务看到其它并发事务更新而到会不一致的数据,通常采用的是LOCK机制。这样付出的代价是,当锁请求无法被响应时,待处理的请求必须进入等候队列,甚至等待超时不被处理。

MVCC通过避开传统数据库的LOCK机制,最大限度的减少锁竞争以允许合理的多用户环境中的性能。

恰当地使用MVCC总会提供比LOCK更好的性能。对于那些无法轻松接收MVCC行为的应用,PostgreSQL也提供了表和行级别的LOCK机制。

PostgreSQL存储结构

PostgreSQL中,一个表对应一个逻辑文件,一个表被分割成若干个物理段文件(relation segment),除最后一段外默认大小40M。

文件页(磁盘块)是物理段文件的基本储存单位,也是内存和磁盘交换的单位。文件页大小限制了表元组的大小并影响磁盘操作效率,缺省大小8192字节,最大可设置为2^15字节(这是由磁盘块索引是15位决定的)。

一个文件页空间被逻辑分割为三个部分:

每个记录的元组(Tuple)称为一项,每项由描述ID和元组数据构成。
项描述ID描述了元组存储位置,大小以及一些状态标识。
项描述ID和项数据分别在元组数据空间的两头往中间存放,最早的项存在最两侧,越晚的数据越靠中间。

PostgreSQL文件页分布.PNG

元组的写过程:先写到文件页的内存缓冲区(Buffer),再更新到磁盘中

文件页的写过程:

MVCC

MVCC(Multiversion Concurrency Control),多版本并发控制。

举一个简单的例子来理解它的机制

inset into T1(id,name) values (1,'zhangsan');
updata T1 set name = 'lisi' where id =1;
Paste_Image.png

举个例子,当insert一行记录时,只有那些已提交的、并且xmin比当前事务XID小(xmin<XID) 的行记录 对当前事务才是可见的。

这意味着你可以创建一个新事务然后插入记录,直到commit之前,这些记录对其他事务永远都是不可见的;commit之后,其他后创建的新事务就可看到这行新记录了(xmin<XID)。

对于deleteupdate,机制也是类似的,不同的是要用xmax值来判断数据的可见性。

隔离级别

SQL标准定义了四个级别的事务隔离。最严格的是可串行化,是通过标准定义,即保证并发执行和顺序执行的结果相同;其他三个级别是通过现象定义的。

隔离级别 脏读 不可重复读 幻读
读未提交(read uncommitted)
读已提交(read committed) 避免
可重复读(repeatable read) 避免 避免
可串行化(serializable) 避免 避免 避免

PostgreSQL中:

SQL标准只定义了那种现象不能发生,但是没定义哪种现象一定发生。

读已提交 可重复读 幻读被避免 直接失败

MVCC实现方法

MVCC的实现方法有两种:

PostgreSQL使用的是第二种方法,Oracle数据库和MySQL innodb引擎使用一种

比较:

存在的问题及解决方法

MVCC实现了一种期待:读永远不堵塞写。但是也带来了一些问题:

  1. 因为不同的事务会看到不同版本的记录,所以PostgreSQL连那些可能过期的数据也要保留着;
    UPDATA时,真正地创建了一行新记录,而DELETE时,并不会真正地删除一行旧记录;
    最终数据库中会存在一些对有事务永远不可见的记录,称作dead rows。
  2. 事务ID只能增加,它是个32bit,支持大约40亿个事务,达到最大值会从0重新开始;
    这样带来一个逻辑问题:突然所有记录都变成了发生在将来的事务所产生的,而所有新事物也都没有办法访问这些旧记录了。
上一篇下一篇

猜你喜欢

热点阅读