深入理解PostgreSQL的MVCC并发处理方式_PostgreSQL

Postgre数据库的很大的卖点之一就是它处理并发的方式。我们的期望很简单:读永远不阻塞写,反之亦然。Postgres通过一个叫做 多版本并发控制(MVCC) 的机制做到了这一点。这个技术并不是Postgres所特有的:还有好几种数据库都实现了不同形式的MVCC,包括 Oracle、Berkeley DB、CouchDB 等等 。当你使用PostgreSQL来设计高并发的应用时,理解它的MVCC是怎么实现的很重要。它事实上是复杂问题的一种非常优雅和简单的解法。

MVCC如何工作

在Postgres中,每一个事务都会得到一个被称作为 XID 的事务ID。这里说的事务不仅仅是被 BEGIN - COMMIT 包裹的一组语句,还包括单条的insert、update或者delete语句。当一个事务开始时,Postgrel递增XID,然后把它赋给这个事务。Postgres还在系统里的每一行记录上都存储了事务相关的信息,这被用来判断某一行记录对于当前事务是否可见。

举个例子,当你插入一行记录时,Postgre会把当前事务的XID存储在这一行中并称之为 xmin 。只有那些*已提交的而且 xmin` 比当前事务的XID小的记录对当前事务才是可见的。这意味着,你可以开始一个新事务然后插入一行记录,直到你提交( COMMIT )之前,你插入的这行记录对其他事务永远都是不可见的。等到提交以后,其他后创建的新事务就可以看到这行新记录了,因为他们满足了 xmin < XID 条件,而且创建哪一行记录的事务也已经完成。

对于 DELETE 和 UPDATE 来说,机制也是类似的,但不同的是对于它们Postgres使用叫做 xmax 的值来判断数据的可见性。这幅图展示了在两个并发的插入/读取数据的事务中,MVCC在事务隔离方面是怎么起作用的。

在下面的图中,假设我们先执行了这个建表语句:

复制代码 代码如下:

CREATE TABLE numbers (value int);

虽然 xmin 和 xmax 的值在日常使用中都是被隐藏的,但是你可以直接请求他们,Postgres会高兴的把值给你:

复制代码 代码如下:

SELECT *, xmin, xmax FROM numbers;

获取当前事务的XID也很简单:

复制代码 代码如下:

SELECT txid_current();

干净利落!

我知道你现在在想:要是同时有两个事务修改同一行数据会怎么样?这就是事务隔离级别(transaction isolation levels)登场的时候了。Postgres支持两个基本的模型来让你控制应该怎么处理这样的情况。默认情况下使用 读已提交(READ COMMITTED) ,等待初始的事务完成后再读取行记录然后执行语句。如果在等待的过程中记录被修改了,它就从头再来一遍。举一个例子,当你执行一条带有 WHERE 子句的 UPDATE 时, WHERE 子句会在最初的事务被提交后返回命中的记录结果,如果这时 WHERE 子句的条件任然能得到满足的话, UPDATE 才会被执行。在下面这个例子中,两个事务同时修改同一行记录,最初的 UPDATE 语句导致第二个事务的 WHERE 不会返回任何记录,因此第二个事务根本没有修改到任何记录:

如果你需要更好的控制这种行为,你可以把事务隔离级别设置为 可串行化(SERIALIZABLE) 。在这个策略下,上面的场景会直接失败,因为它遵循这样的规则:“如果我正在修改的行被其他事务修改过的话,就不再尝试”,同时 Postgres会返回这样的错误信息: 由于并发修改导致无法进行串行访问 。捕获这个错误然后重试就是你的应用需要去做的事情了,或者不重试直接放弃也行,如果那样合理的话。

MVCC的缺点

现在你已经知道MVCC和事务隔离是怎么工作了吧,你获得了又一个工具用来解决这类问题: 可串行化事务隔离级别 迟早会派上用场。然而MVCC的优点虽然很明显但它也存在着一些缺点。

因为不同的事务会看到不同状态的记录,Postgres连那些可能过期的数据也需要保留着。这就是为什么 UPDATE 实际上是创建一行新纪录而 DELETE 并不真正的删除记录(它只是简单的把记录标记成已删除然后设置XID的值)的原因。当事务完成后,数据库里会存在一些对以后的事务永远不可见的记录。它们被称作dead rows。MVCC带来的另外一个问题是,事务的ID只能不断的增加 - 它是32个bits,只能”支持大约四十亿个事务。当XID达到最大值后,它会变回零重新开始。突然间所有的记录都变成了发生在将来的事务所产生的,所有的新事务都没有办法访问到这些旧记录了。

上面说到的dead row和事务XID循环问题都是通过执行VACUUM命令(Postgres用来执行清理操作的命令)来解决的。这应该成为一个例行的维护,所以Postgre自带了auto_vacuum守护进程会在一个可配置的周期内自动执行清理。留意点auto_vacuum很重要,因为在不同的部署环境中需要执行清理的周期也会不同。你可以在Postgres的文档里找到关于VACUUM的更多说明。

时间: 2024-10-01 16:38:30

深入理解PostgreSQL的MVCC并发处理方式_PostgreSQL的相关文章

简单理解PHP的面向对象编程方式_php基础

与大多数可以面向对象的编程语言不一样, PHP 是同时支持面向过程和面向对象的编程方式, PHP 开发者可以在面向过程和面向对象二者中自由选择其一或是混合使用,不过由于在 PHP5 之前的版本中, PHP 主要还是面向过程的编程语言,因此大多时候 PHP 开发者应该还是选择面向过程的方式进行开发,事实上, Kayo 认为即使一个 PHP 开发者完全不使用面向对象,他也能开发出很出色的 PHP 程序,我们可以想象, Web 页面的解析本身就很过程化,在 HTML 中嵌入面向过程处理的代码是非常自然

深入理解jQuery.data() 的实现方式_jquery

jQuery.data() 的作用是为普通对象或 DOM Element 附加(及获取)数据.  下面将分三个部分分析其实现方式:  1. 用name和value为对象附加数据:即传入三个参数,第一个参数为需要附加数据的对象,第二个参数为数据的名称,第三个参数为数据的值.当然,只是获取值的话,也可以不传入第三个参数.  2. 用另一个对象为对象附加数据:即传入两个参数,第一个参数为需要附加的数据对象(我们称之为"obj"),第二个参数也是一个对象(我们称之为"another&

PostgreSQL教程(十一):服务器配置_PostgreSQL

一.服务器进程的启动和关闭:     下面是pg_ctl命令的使用方法和常用选项,需要指出的是,该命令是postgres命令的封装体,因此在使用上比直接使用postgres更加方便. 复制代码 代码如下:     pg_ctl init[db] [-D DATADIR] [-s] [-o "OPTIONS"]     pg_ctl start     [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o "OPTIONS&quo

深入理解.NET 的JIT编译方式

编译 CLR只执行本机的机器代码.有两种方式产生本机的机器代码:实时编译(JIT)和预编译方式(产生native image).下面,我想谈谈JIT.CLR使用类型的方法表来路由所有的方法调用.类型的方法表由多个入口项组成.每个入口项指向一个唯一的存根例程(stub routine).初始化时,每个存根例程包含一个对于CLR的JIT编译器的调用(它由内部的PreStubWorker程序公开).在JIT编译器生成本机代码后,它会重写存根例程,插入一个jmp指令跳转到刚才JIT编译器的代码.只有当要

快速理解 session/token/cookie 认证方式

目录 目录 cookie session token cookie Web Application 一般以 HTTP 协议作为传输协议, 但 HTTP 协议是无状态的. 也就是说 server-side 与 client-side 一旦数据交换完毕后,两者之间的连接就会被关闭. client-side 再次发送请求时, 需要建立新的连接, 这就意味着 server-side 和 client-side 两者之间无法通过 HTTP 的连接来实现 会话跟踪. 显然, 这是不合理的, 因为这样无法保证

15个postgresql数据库实用命令分享_PostgreSQL

最初是想找postgresql数据库占用空间命令发现的这篇blog,发现其中提供的几 条命令很有用(但也有几条感觉是充数的=.=),于是就把它翻译过来了.另外这篇文章是09年的,所以里面的内容可能有点过时,我收集了原文中有用的评论放在了最后面. 现在有不少开源软件都在使用postgreSQL作为它们的数据库系统.但公司可能不会招一些全职的postgreSQL DBA来维护它(piglei: 在国内基本也找不到).而会让一些比如说Oracle DBA.Linux系统管理员或者程序员去 维护.在这篇

memcached实战系列(七)理解Memcached的数据过期方式、新建过程、查找过程

1.1.1. 新建Item分配内存过程 1:快速定位slab classid,先计算Item长度 key键长+flag+suffix(16字节)+value值长+结构大小(32字节),如90byte 如果>1MB,无法存储丢弃 取最小冗余的slab class 如:有48,96,120,存90会选择96   1.1.2. 按顺序寻找可用chunk顺序 (1)slot:检查slab回收空间slot里是否有剩余chunk delete:delete时标记到slot exptime:get时检查的过期

memcached实战系列(六)理解Memcached的数据存储方式

Memcached的数据存储方式被称为Slab Allocator,其基本方式是: 1:先把内存分成很多个Slab,这个大小是预先规定好的,以解决内存碎片的问题.启动参数的时候配置进去的不懂得可以参考memcached启动参数配置章节. 分配给Slab的内存空间被称为Page,默认是1M.一个Slab下可以有多个Page. 2:然后把一个Page分成很多个chunk块,chunk块是用于缓存记录的空间.Chunk的 大小是先有一个基本值,然后根据增长因子来增大.每一个page中chunk是相等的

PgSQL · 特性分析 · MVCC机制浅析

背景 我们在使用PostgreSQL的时候,可能会碰到表膨胀的问题(关于表膨胀可以参考之前的月报),即表的数据量并不大,但是占用的磁盘空间比较大,查询比较慢.为什么PostgreSQL有可能发生表膨胀呢?这是因为PostgreSQL引入了MVCC机制来保证事务的隔离性,实现数据库的隔离级别. 在数据库中,并发的数据库操作会面临脏读(Dirty Read).不可重复读(Nonrepeatable Read).幻读(Phantom Read)和串行化异常等问题,为了解决这些问题,在标准的SQL规范中