码迷,mamicode.com
首页 > 数据库 > 详细

PostgreSQL的并发控制

时间:2015-08-27 15:34:31      阅读:4505      评论:0      收藏:0      [点我收藏+]

标签:

9.3中文文档:http://58.58.27.50:8079/doc/html/9.3.1_zh/mvcc.html

参考博客:中文:http://www.zlovezl.cn/articles/postgresql-concurrency-with-mvcc/

                英文:https://devcenter.heroku.com/articles/postgresql-concurrency


  本文描述PostgreSQL数据库系统在多个会话试图同时访问同一数据时的表现。并发控制的目标是为所有会话提供高效的访问,同时还要维护严格的数据完整性。每个数据库应用开发人员都应该熟悉本话题。

关于PostgreSQL并发控制的介绍

   PostgreSQL为开发者提供了丰富的对数据并发访问进行管理的工具。 在内部,PostgreSQL利用多版本并发控制(MVCC)来维护数据的一致性。这就意味着当检索数据时,每个事务看到的都只是一小段时间之前的数据快照(一个数据库版本),而不是数据的当前状态。这样,如果对每个数据库会话进行事务隔离,就可以避免一个事务看到其它并发事务的更新而导致不一致的数据。MVCC通过避开传统数据库系统锁定的方法,最大限度地减少锁竞争以允许合理的多用户环境中的性能。

  使用多版本并发控制比锁定模型的主要优点是在MVCC里,对检索(读)数据的锁请求与写数据的锁请求不冲突,所以读不会阻塞写,而写也从不阻塞读。甚至当通过创新的序列化快照隔离(SSI)级别提供事务隔离的严格等级时,PostgreSQL维持这样的保证。

  在PostgreSQL里也有表和行级别的锁定机制,用于给那些无法轻松接受MVCC行为的应用。 不过,恰当地使用MVCC总会提供比锁更好的性能。另外,由应用定义的咨询锁提供了一个获得不依赖于单独事务的锁的机制。


一、事务隔离

  SQL标准定义了四个级别的事务隔离。最严格的是串行化,它是通过标准来定义的,也就是说, 保证一组可序列化事务的并发执行以产生同样顺序依次运行它们的同一效果。 其他三个层次是通过现象术语被定义,导致并发事务之间的相互作用,这不应该发生在每个级别中。

  各个级别不希望发生的现象是:

  脏读:一个事务读取了另一个未提交事务写入的数据。

  不可重复读:一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交事务修改。

  幻读:一个事务重新执行一个查询,返回一套符合查询条件的行,发现这些行因为其它最近提交的事务而发生了改变。

  这四种隔离级别和对应的行为在表Table 13-1里描述。

  Table 13-1. 标准SQL事务隔离级别

隔离级别  脏读     不可重复读   幻读

读未提交  可能     可能      可能

读已提交  不可能     可能      可能

可重复读  不可能   不可能      可能

可串行化  不可能    不可能      不可能

  在PostgreSQL里,你可以请求四种可能的事务隔离级别中的任意一种。但是在内部, 实际上只有三种独立的隔离级别,分别对应读已提交,可重复读和可串行化如果你选择了读未提交的级别, 实际上你用的是读已提交,在重复读的PostgreSQL执行时,幻读是不可能的, 所以实际的隔离级别可能比你选择的更严格。这是 SQL 标准允许的:四种隔离级别只定义了哪种现像不能发生, 但是没有定义那种现像一定发生。PostgreSQL只提供两种隔离级别的原因是, 这是把标准的隔离级别与多版本并发控制架构映射相关的唯一合理方法。可用的隔离级别的行为在下面小节里描述。

   要设置一个事务的隔离级别,使用SET TRANSACTION命令。

   特别注意: 一些PostgreSQL数据类型和函数关于事务行为有特定的规则。 尤其是,序列变化(因此列数通过serial声明)对于所有其他的事务是立即可见的, 如果事务改变终止,则不进行回退。参见Section 9.16Section 8.1.4


事务的开启和结束命令:http://58.58.27.50:8079/doc/html/9.3.1_zh/sql-set-transaction.html

begin; --开启事务,可以在此指定事务隔离级别。
......

end;/commit;  --- 结束并提交事务
rollback;--结束并回滚事务


事务隔离级别详解

读已提交隔离级别( BEGIN TRANSACTION ISOLATION LEVEl READ COMMITTED; )

   是PostgreSQL里的缺省隔离级别。

   当一个事务运行在这个隔离级别时: SELECT查询(没有FOR UPDATE/SHARE子句)只能看到查询开始之前已提交的数据而无法看到未提交的数据或者在查询执行期间其它事务已提交的数据 (仅读当时数据库快照)。不过,SELECT看得见其自身所在事务中前面更新执行结果。即使它们尚未提交。(注意:在同一个事务里两个相邻的SELECT命令可能看到不同的快照,因为其它事务会在第一个SELECT执行期间提交。

技术分享

   (对于insert、update,delete操作: 如果表有主键或者唯一约束,那么一旦其他事物执行了未提交的insert,当前事物中的insert将会等待其他提交之后再提交;否则。可以直接执行。)


可重复读隔离级别(BEGIN TRANSACTION ISOLATION LEVEl REPEATABLE READ; )

  这个级别和读已提交级别是不一样的。重复读事务中的查询看到的是事务开始时的快照, 而不是该事务内部当前查询开始时的快照,这样, 同一个事务内部后面的SELECT命令总是看到同样的数据等,它们没有看到通过 自身事务开始之后提及的其他事务做出的改变。

  使用这个级别的应用必须准备好重试事务,因为串行化失败。

技术分享

可串行化隔离级别:(BEGIN TRANSACTION ISOLATION LEVEl SERIALIZABLE; )

可串行化级别提供最严格的事务隔离。这个级别为所有已提交事务模拟串行的事务执行, 就好像事务将被一个接着一个那样串行(而不是并行)的执行。不过,正如可重复读隔离级别一样, 使用这个级别的应用必须准备在串行化失败的时候重新启动事务。 事实上,该隔离级别和可重复读希望的完全一样, 它只是监视这些条件,以所有事务的可能的序列不一致的(一次一个)的方式执行并行的可序列化事务执行的行为。 这种监测不引入任何阻止可重复读出现的行为,但有一些开销的监测,检测条件这可能会导致序列化异常 将触发序列化失败

技术分享

   特别注意:序列化事务隔离级别尚未被添加到热备复制目标中 (正如在Section 25.5中描述的)。 严格的隔离级别目前热备方式上支持可重复读。 当在主库上执行所有永久数据库写入可序列化事务中将确保所有的措施将最终达成一致, 运行在备库上的可重复读事务会看到一个过渡状态,与主库上的任何串行执行的可序列化事务不一致。

 


二、显式锁定

    PostgreSQL提供了多种锁模式用于控制对表中数据的并发访问。 这些模式可以用于在MVCC无法给出期望行为的场合。同样, 大多数PostgreSQL命令自动施加恰当的锁以保证被引用的表在命令的执行过程中不会以一种不兼容的方式被删除或者修改。 比如,在存在其它并发操作的时候,TRUNCATE是不能在同一个表上面执行的

    要检查数据库服务器里所有当前正在被持有的锁, 可以使用pg_locks系统视图。 有关监控锁管理器子系统状态的更多信息,请参考章Chapter 27

   下面的列表显示了可用的锁模式和它们被PostgreSQL自动使用的场合。 你也可以用LOCK命令明确获取这些锁。两个事务在同一时刻不能在同一个表上持有相互冲突的锁。不过,一个事务决不会和自身冲突。比如,它可以在一个表上请求ACCESS EXCLUSIVE然后接着请求 ACCESS SHARE。 非冲突锁模式可以被许多事务同时持有。请特别注意有些锁模式是自冲突的(比如,在任意时刻ACCESS EXCLUSIVE模式就不能够被多个事务拥有), 但其它锁模式都不是自冲突的(比如,ACCESS SHARE可以被多个事务持有)。

表级锁:

    ACCESS SHARE:

只与ACCESS EXCLUSIVE冲突。

SELECT命令在被引用的表上请求一个这种锁。通常, 任何只读取表而不修改它的命令都请求这种锁模式。

    ROW SHARE:

EXCLUSIVEACCESS EXCLUSIVE锁模式冲突。

SELECT FOR UPDATESELECT FOR SHARE命令在目标表上需要一个这样模式的锁 (加上在所有被引用但没有ACCESS SHARE的表上的FOR UPDATE/FOR SHARE锁)。

    ROW EXCLUSIVE:

SHARESHARE ROW EXCLUSIVEEXCLUSIVE和 ACCESS EXCLUSIVE锁模式冲突。

UPDATE,DELETEINSERT命令自动请求这个锁模式(加上所有其它被引用的表上的ACCESS SHARE锁)。 通常,这种锁将被任何修改表中数据的查询请求。

    SHARE UPDATE EXCLUSIVE:

SHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE和 ACCESS EXCLUSIVE锁模式冲突。 这个模式保护一个表不被并发模式改变和VACUUM

VACUUM(不带FULL选项), ANALYZECREATE INDEX CONCURRENTLYALTER TABLE请求这样的锁。

    SHARE:

ROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARE ROW EXCLUSIVEEXCLUSIVE和 ACCESS EXCLUSIVE锁模式冲突。 这个模式避免表的并发数据修改。

CREATE INDEX(不带CONCURRENTLY选项)语句要求这样的锁模式。

    SHARE ROW EXCLUSIVE:

ROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE和 ACCESS EXCLUSIVE锁模式冲突。这个模式避免表的并发数据修改。 并且是自我排斥的,因此每次只有一个会话可以拥有它。

任何PostgreSQL命令都不会自动请求这个锁模式。

    EXCLUSIVE:

与 ROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE和 ACCESS EXCLUSIVE锁模式冲突。这个模式只允许并发ACCESS SHARE锁,也就是说, 只有对表的读动作可以和持有这个锁模式的事务并发执行。

任何PostgreSQL命令都不会在用户表上自动请求这个锁模式。

    ACCESS EXCLUSIVE:

与所有模式冲突(ACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE和 ACCESS EXCLUSIVE)。这个模式保证其所有者(事务)是可以访问该表的唯一事务。

ALTER TABLEDROP TABLETRUNCATEREINDEXCLUSTERVACUUM FULL命令要求这样的锁。 在LOCK TABLE命令没有明确声明需要的锁模式时,它是缺省锁模式。

Tip: 只有ACCESS EXCLUSIVE阻塞SELECT (不包含FOR UPDATE/SHARE语句)。

一旦请求已获得某种锁,那么该锁模式将持续到事务结束。但是如果在建立保存点之后才获得锁, 那么在回滚到这个保存点的时候将立即释放所有该保存点之后获得的锁。这与ROLLBACK取消所有保存点之后对表的影响的原则一致。 同样的原则也适用于PL/pgSQL异常块中获得的锁: 一个跳出块的错误将释放在块中获得的锁。

行级锁:

  行级锁可以是排他的或者是共享的。特定行上的排他行级锁是在行被更新的时候自动请求的。 该锁一直保持到事务提交或者回滚。行级锁不影响对数据的查询,它们只阻塞对同一行的写入

  行级锁语法:http://58.58.27.50:8079/doc/html/9.3.1_zh/sql-select.html

  FOR UPDATEFOR NO KEY UPDATEFOR SHARE 和 FOR KEY SHARE锁定子句;他们影响 SELECT如何从表中锁定行作为获得的行。锁定子句的一般形式:

FOR lock_strength [ OF table_name [, ...] ] [ NOWAIT ]

这里的lock_strength可以是下列之一:

UPDATE
NO KEY UPDATE
SHARE
KEY SHARE

  FOR UPDATE令那些被SELECT检索出来的行被锁住, 就像要更新一样。这样就避免它们在当前事务结束前被其它事务修改或者删除;也就是说, 其它企图UPDATEDELETESELECT FOR UPDATESELECT FOR NO KEY UPDATESELECT FOR SHARE 或 SELECT FOR KEY SHARE这些行的事务将被阻塞,直到当前事务结束。同样, 如果一个来自其它事务的UPDATEDELETESELECT FOR UPDATE已经锁住了某个或某些选定的行,SELECT FOR UPDATE 将等到那些事务结束,并且将随后锁住并返回更新的行(或者不返回行,如果行已经被删除)。 但是,在REPEATABLE READSERIALIZABLE事务内部, 如果在事务开始时要被锁定的行已经改变了,那么将抛出一个错误。更多的讨论参阅Chapter 13

  FOR NO KEY UPDATE的行为类似,只是获得的锁比较弱:这个锁将不锁定尝试在相同的行上获得锁的SELECT FOR KEY SHARE命令。这个锁模式也可以通过任何不争取FOR UPDATE锁的UPDATE获得。

  FOR SHARE的行为类似,只是它在每个检索出来的行上要求一个共享锁,而不是一个排它锁。一个共享锁阻塞其它事务在这些行上执行UPDATEDELETESELECT FOR UPDATE 或 SELECT FOR NO KEY UPDATE,却不阻止他们执行SELECT FOR SHARE 或 SELECT FOR KEY SHARE

  FOR KEY SHARE的行为类似于FOR SHARE,只是锁比较弱: 阻塞SELECT FOR UPDATE但不阻塞SELECT FOR NO KEY UPDATE。一个共享键块阻塞其他事务执行DELETE或任意改变键值的UPDATE, 但是不阻塞其他UPDATE,也不阻止SELECT FOR NO KEY UPDATESELECT FOR SHARE, 或 SELECT FOR KEY SHARE


死锁:

 显式锁定的使用可能会增加死锁的可能性,死锁是指两个(或多个)事务相互持有对方期待的锁。


咨询锁:

 PostgreSQL允许创建由应用定义其含义的锁。这种锁被称为咨询锁, 因为系统并不强迫其使用— 而是由应用来保证其被恰当的使用。 咨询锁可用于 MVCC 难以实现的锁定策略。 比如,咨询锁一般用于模拟常见于"平面文件"数据管理系统的悲观锁策略。 虽然可以用存储在表中的一个特定标志达到同样的目的,但是使用咨询锁更快,还可以避免表臃肿, 更可以在会话结束的时候由系统自动执行清理工作。 



PostgreSQL的并发控制

标签:

原文地址:http://my.oschina.net/liuyuanyuangogo/blog/497929

(0)
(1)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!