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

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的更多说明。

(0)

相关推荐

  • VC++操作SQLite简单实例

    对于很多程序员来说,SQLite并不陌生.SQLite 是一款主要用于嵌入式的开源数据库,可集成在自己的桌面程序中,也可替代Access作为后台数据库.SQLite 支持多数SQL92标准,例如:索引.限制.触发和查看支持,支持 NULL.INTEGER.REAL.TEXT 和 BLOB 数据类型,支持事务.以下是一些SQLite的简单使用. //建立数据库 C:\sqlite-3_6_11> sqlite3.exe dbname.db 建立数据库的时候sqlite3.exe后面跟数据库文件名

  • 解决springmvc+mybatis+mysql中文乱码问题

    近日使用ajax请求springmvc后台查询mysql数据库,页面显示中文出现乱码 最初在mybatis配置如下 <select id="queryContentById" resultType = "java.lang.String" parameterType="String" > select text from News where id=#{o} </select> 其中表News的text字段为blob类型

  • VC实现ODBC数据库操作实例解析

    本文以VC实例介绍了实现数据库操作的各种方法,包括打开数据库.关联记录集.读取Excel数据.执行SQL查询.创建存取字符串等,相信对于初学VC的朋友有一定的参考价值,主要功能代码如下: //打开数据库 CDatabase database; database.OpenEx( _T( "DSN=zhuxue" ),CDatabase::noOdbcDialog);//zhuxue为数据源名称 //关联记录集 CRecordset recset(&database); //查询记

  • asp.net mvc 从数据库中读取图片的实现代码

    首先是创建一个类,继承于ActionResult,记住要引用System.Web.Mvc命名空间,如下: 复制代码 代码如下: public class ImageResult : ActionResult { public ImageFormat ContentType { get; set; } public Image image { get; set; } public string SourceName { get; set; } public ImageResult(string _

  • VC基于ADO技术访问数据库的方法

    本文实例讲述了VC基于ADO技术访问数据库的方法.分享给大家供大家参考.具体如下: 一.在StdAfx.h文件中添加 复制代码 代码如下: #import "C:/Program Files/Common Files/System/ado/msado15.dll" no_namespace rename("EOF","rsEOF") 导入ADO引擎. 二.数据库应用层操作 void CADOExample1Dlg::OnBtnQuery() {

  • VC用Ado接口连接和使用数据库及注意事项

    一.阅读本文所需注意的其它事项 1.进行方法调用时,所传递的参数的类型的转换(可能存在比本文更简便的处理方法但我未发现) 2.每个源文件的每行注释说明了其文件名 3.请关注相关头文件包含关系 4.请关注文中所有中文注释 5.更多的内容请参阅 "VC安装目录\Include\adoint.h"文件,adoint即ActiveX Data Object InterFace(菜 鸟请勿惊慌,这仅仅只是个名称) 二.下面的源文件与您的数据库应用程序不直接相关,但其目标代码(生成的.obj文件)

  • 让Sqlite脱离VC++ Runtime独立运行的方法

    目前手头上的一个.NET项目用到了Sqlite,这是我第一次接触和使用Sqlite,总体感觉还是非常不错的.这里把我使用Sqlite的经验跟大家分享一下. 一.关于Sqlite Sqlite是一款开源的.适合在客户端和嵌入式设备中使用的轻量级数据库,支持标准的SQL. 不像SqlServer或Oracle的引擎是一个独立的进程.通过TCP或命名管道等与程序进行通信,SQLite却是作为程序的一个部件.一个构成部分,使用Sqlite的方式就是直接在程序中进行API调用. 原始的Sqlite是没有一

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

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

  • postgresql 性能参数配置方式

    查询Linux服务器配置 在进行性能调优之前,需要清楚知道服务器的配置信息,比如说 CPU,内存,服务器版本等信息. 查询系统版本信息 root@ubuntu160:~# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 14.04.3 LTS Release: 14.04 Codename: trusty Linux查看物理CPU个数.核数.逻辑CPU个数 # 总核数

  • 深入理解MySQL中MVCC与BufferPool缓存机制

    目录 一.MVCC机制 undo日志版本链与read-view机制 版本链比对规则 二.BufferPool机制 三.总结 一.MVCC机制 MVCC(Multi Version Concurrency Control),MySQL(默认)RR隔离级别就是通过该机制来保证的,对一行数据的读与写两个操作默认是不会通过加锁互斥来保证隔离性的 串行化隔离级别是为了保证较高的隔离性,是通过将所有操作加锁互斥来实现的 MySQL在RC隔离级别和RR隔离级别下都实现了MVCC机制 RC每次查询都会创建一个r

  • 简单理解PHP的面向对象编程方式

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

  • 重新理解JavaScript的六种继承方式

    类式继承(构造函数) JS中其实是没有类的概念的,所谓的类也是模拟出来的.特别是当我们是用new 关键字的时候,就使得"类"的概念就越像其他语言中的类了.类式继承是在函数对象内调用父类的构造函数,使得自身获得父类的方法和属性.call和apply方法为类式继承提供了支持.通过改变this的作用环境,使得子类本身具有父类的各种属性. var father = function() { this.age = 52; this.say = function() { alert('hello

  • PostgreSQL 默认权限查看方式

    如何查看PostgreSQL默认权限 当我们对Postgresql的某个用户授予默认权限时, pg_default_acl表存储要被分配给新创建对象的初始权限.你可能通过查询该表获取默认权限,先了解下官方文档的解释: 表 50-17. pg_default_acl的列: 名称 类型 引用 描述 oid oid 行标识符(隐藏属性,必须被显式选择才会显示) defaclrole oid pg_authid.oid 与此项相关的角色的OID defaclnamespace oid pg_namesp

  • 深入理解JavaScript创建对象的多种方式以及优缺点

    写在前面 这篇文章讲解创建对象的各种方式,以及优缺点. 但是注意: 这篇文章更像是笔记,因为<JavaScript高级程序设计>写得真是太好了! 1. 工厂模式 function createPerson(name) { var o = new Object(); o.name = name; o.getName = function () { console.log(this.name); }; return o; } var person1 = createPerson('kevin');

  • 深入理解JavaScript继承的多种方式和优缺点

    写在前面 本文讲解JavaScript各种继承方式和优缺点. 注意: 跟<JavaScript深入之创建对象>一样,更像是笔记. 哎,再让我感叹一句:<JavaScript高级程序设计>写得真是太好了! 1.原型链继承 function Parent () { this.name = 'kevin'; } Parent.prototype.getName = function () { console.log(this.name); } function Child () { }

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

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

  • 深入理解spring boot异步调用方式@Async

    本文主要给大家介绍了关于spring boot异步调用方式@Async的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 1.使用背景 在日常开发的项目中,当访问其他人的接口较慢或者做耗时任务时,不想程序一直卡在耗时任务上,想程序能够并行执行,我们可以使用多线程来并行的处理任务,也可以使用spring提供的异步处理方式@Async. 2.异步处理方式 调用之后,不返回任何数据. 调用之后,返回数据,通过Future来获取返回数据 3.@Async不返回数据 使用@EnableAsyn

随机推荐