PostgreSQL并发处理方式——MVCC

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

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

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

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

PostgreSQL存储结构

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

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

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

  • PageHeader:页描述区
  • 记载页的使用情况, 如页分布格式版本,元组数据空间和特殊空间的起始位置以及文件页相关的事务日志记载点等信息
  • Tuple Item space:元组数据空间
  • 实际记录元组数据的地方
  • Special space:特殊空间

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

PostgreSQL文件页分布.PNG

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

  • 从缓冲页的元组数据存储区分配空间
  • 构造元组描述ID,写入低端处
  • 把实际数据写到高端处,并设置缓冲区的脏标记
  • 更新到磁盘
  • 写元组不会立即更新到磁盘,而是推迟到所在的缓冲区被替换(Replace)时进行
  • Replace时,判断缓冲区是否脏:
  • 如果脏,启动实际磁盘IO进行写;
  • 如果不脏,直接回收再用该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
  • 每个事务都会得到一个XID(称为事务ID),当一个新事务开始,递增XID,然后把它赋予这个新事务。
  • 把一个元组(Tuple)称作同一逻辑行的一个行版本,数据文件中存放同一逻辑行的多个行版本
  • 每个行版本的头部,记录该行版本的创建和删除的事务ID(分别称为xmin和xmax)
  • 每个事务的状态(running, abort 或 commit)记录在pg_clog文件中
  • 运用一定的规则,使每个事务只会看到一个特定的行版本(快照)

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

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

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

隔离级别

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

隔离级别 脏读 不可重复读 幻读
读未提交(read uncommitted)
读已提交(read committed) 避免
可重复读(repeatable read) 避免 避免
可串行化(serializable) 避免 避免 避免
  • 幻读:重新执行一个查询,由于最近另一个事务的提交,返回的结果(一批数据)和刚才不同;
  • 不可重复读: 针对同一个数据,一个事务内多次查询,由于期间另一个事务的提交,导致结果(同一个数据)不同;
  • 脏读:一个事务读取了另一个事务还未提交的改动。

PostgreSQL中:

  • 默认隔离级别是读已提交(read committed);
  • 可以请求四中级别的任意一种,但对于内部其实只有读已提交、可重复读、可串行化三种级别;
  • 选择读未提交时,实际上用的是读已提交;
  • 选择重复读时,不会发生幻读;

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

  • 读已提交
  • BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
  • PostgreSQL里的缺省隔离级别
  • 看到的是当前查询开始时的快照
  • 当有两个事务同时修改同一行数据时,后发生的事务在初始事务提交前就可以进行查找,然后不执行进入等待,待初始事务提交后retry,检验查找条件是否仍然满足,如果满足,后续操作才会被执行;
  • 例如下图中的例子:
    在事务1提交之前,通常LOCK机制会让事务2进入等待,到事务1提交后才可以扫描查找;
    而MVCC允许事务2在事务1提交之前就可以进行扫描查找工作,当事务1提交之后,事务2retry,检验where条件是否仍然满足;
    若不满足,则不会执行任何操作(保证了一致性);
    若满足,则相比LOCK机制节省了扫描查找所消耗的时间(在LOCK机制等待commit时MVCC就开始扫描查找了)。
读已提交
  • 可重复读
  • BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
  • 看到的是当前事务开始时的快照
  • 使用这个级别需要准备好重试事务,因为串行化可能失败
  • 在第二张图的例子中,按照一般的LOCK机制,在可重复读的级别下,在事务B提交后,查询结果应该不同(即幻读),但是在MVCC机制中,查询结果是相同的(幻读也被避免了)
可重复读
幻读被避免
  • 可串行化
  • BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
  • 是严格意义上的可串行化
  • 可重复读级别已经避免了幻读,达到了SQL标准约定的“可串行化标准”,但能避免幻读并不等于严格意义上的可串行化
  • 在这种策略下,不同事务同时修改同一数据的行为会直接失败,并返回错误信息
  • 需要准备好重启事务
直接失败

MVCC实现方法

MVCC的实现方法有两种:

  • 写新数据是,把旧数据移到一个专门的地方(如回滚段),其他人读数据时,从回滚段中把旧数据读出来
  • 写数据时,旧数据不删除,把新数据插入

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

比较:

  • 优点:
  • 回滚可以立刻完成,无论进行了多少操作
  • 数据可以进行逆很多更新,不必担心需要保证回滚段不被用完
  • 缺点:
  • 旧版本数据需要清理
  • 旧版本数据过多导致查询变慢

存在的问题及解决方法

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

  1. 因为不同的事务会看到不同版本的记录,所以PostgreSQL连那些可能过期的数据也要保留着;
    UPDATA时,真正地创建了一行新记录,而DELETE时,并不会真正地删除一行旧记录;
    最终数据库中会存在一些对有事务永远不可见的记录,称作dead rows。
  2. 事务ID只能增加,它是个32bit,支持大约40亿个事务,达到最大值会从0重新开始;
    这样带来一个逻辑问题:突然所有记录都变成了发生在将来的事务所产生的,而所有新事物也都没有办法访问这些旧记录了。
  • 解决方法:VACUUM
    PostgreSQL自带了auto_vacuum守护进程会在一个可配置的周期内自动执行清理,解决了这两个问题;
    使用者需要留意这个auto_vacuum,以免发生不想要的结果;
    vacuum命令也可以手动执行。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容