PostgreSQL7.0手册-用户手册-13. 多版本并行控制
第十三章. 多版本并行控制
内容
介绍
事务隔离
读已提交隔离级别
可串行化隔离级别
锁和表
锁和索引
应用层的数据完整性检查
多版本并行控制(Multi-Version Concurrency Control (MVCC))是用于提高数据库在一个多用户环境下性能的高级技术。Vadim Mikheev 为 Postgres 提供了MVCC的实现。
介绍
与其他使用锁来进行并行控制的数据库系统不同,Postgres 利用多版本模型来维护数据的一致性。这就意味着当检索数据库时,每个事务都看到一个数据的一段时间前的快照(一个 数据库版本),而不管正在处理的数据当前的状态。这样,如果对每个数据库会话进行 事务隔离,就可以避免一个事务看到因为其它并行的事务更新同一行数据而导致的不连贯的数据。
多版本和锁定模型的主要区别是在 MVCC 里,对检索(读)数据的锁要求与写数据的锁要求不冲突,所以读不会阻塞写,而写也从不阻塞读。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
事务隔离
ANSI/ISOSQL 用三个必须在并行的事务之间避免的现象定义了四个级别的事务隔离。这些不希望发生的现象是:
读污染(dirty reads)
一个事务读取了被另一个未提交的并行的事务写的数据。
不可重复的读(non-repeatable reads)
一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过。
错误读取(phantom read)
一个事务重新执行一个查询,返回一套符合查询条件的行,发现这些行中插入了被其他已提交的事务提交的行。
这四种隔离级别和对应的特性在下面描述。
表 13-1. Postgres 隔离级别
读污染(Dirty Read) 不可重复的读(Non-Repeatable Read) 错误读取(Phantom Read)
读未提交(Read uncommitted) 可能 可能 可能
读已提交(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能
Postgres 提供读已提交(read committed)和可串行化(serializable)隔离级别。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
读已提交隔离级别
读已提交(Read Committed)是Postgres 里的缺省隔离级别。当一个事务运行在这个隔离级别时,一个查询只能看到查询开始之前的数据而永远无法看到脏数据或者是在查询执行时其他并行的事务提交做的改变。
如果一个正在执行一个 UPDATE 语句(或者 DELETE 或者 SELECT FOR UPDATE)的查询返回的行正在被另一个并行的未提交的事务更新,那么第二个试图更新此行的事务将等待另一个事务的提交或者回卷。如果发生了回卷,等待中的事务可以继续修改此行。如果发生了提交(并且此行仍然存在;也就是说,没有被另一个事务删除),这个查询将对该行再执行一便以检查新行版本是否满足查询搜索条件。如果新行版本满足查询搜索条件,那么该行将被更新(或删除或被标记为更新)。
注意 SELECT 或 INSERT 语句执行的结果(在一个查询里)将不会被并行事务影响。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
可串行化隔离级别
可串行化(Serializable)提供最高级别的事务隔离。当一个事务处于可串行化级别,一个查询只能看到在事务开始之前提交的数据而永远看不到脏数据或事务执行中其他并行事务提交的修改。所以,这个级别模拟串行事务执行,就好象事务将被一个接着一个那样串行的,而不是并行的执行。
如果一个正在执行一个 UPDATE 语句(或者 DELETE 或者 SELECT FOR UPDATE)的查询返回的行正在被另一个并行的未提交的事务更新,那么第二个试图更新此行的事务将等待另一个事务的提交或者回卷。如果发生了回卷,等待中的事务可以继续修改此行。如果发生一个并行的事务的提交,一个可串行化的事务将回卷,并返回下面信息。
ERROR: Can't serialize access due to concurrent update
因为一个可串行化的事务在可串行化事务开始之后不能更改被其他事务更改过的行。
注意: SELECT 或 INSERT 语句执行的结果(在一个查询里)将不会被并行事务影响。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
锁和表
Postgres 提供多种锁模式来控制对表中数据的并行访问。有些锁模式是在语句执行之前由Postgres 自动施加的,而其他的提供用来由应用使用的。一个事务里要求的所有锁模式(除了 AccessShareLock)都是在整个事务期间保有的。
除了锁以外,短期的共享/排他销也在共享的缓冲池中用于控制对表页面的读/写访问。销在一条记录抓取或者更新后马上被释放。
表级锁
AccessShareLock
一个内部锁模式,进行查询时自动施加在被查询的表上。语句执行完成后,Postgres 释放这些锁。
只与 AccessExclusiveLock 冲突。
RowShareLock
被 SELECT FOR UPDATE 和用于 IN ROW SHARE MODE 的 LOCK TABLE 语句要求。
与 ExclusiveLock 和 AccessExclusiveLock 模式冲突。
RowExclusiveLock
被 UPDATE, DELETE, INSERT 和用于IN ROW EXCLUSIVE MODE 的 LOCK TABLE 语句要求。
与 ShareLock,ShareRowExclusiveLock,ExclusiveLock 和 AccessExclusiveLock 模式冲突。
ShareLock
被 CREATE INDEX 和用于IN SHARE MODE 的 LOCK TABLE 语句所要求。
与 RowExclusiveLock,ShareRowExclusiveLock,ExclusiveLock 和 AccessExclusiveLock 模式冲突。
ShareRowExclusiveLock
被用于 IN SHARE ROW EXCLUSIVE MODE 的 LOCK TABLE 语句要求。
与 RowExclusiveLock,ShareLock,ShareRowExclusiveLock,ExclusiveLock 和 AccessExclusiveLock 模式冲突。
ExclusiveLock
被用于 IN EXCLUSIVE MODE 的 LOCK TABLE 语句要求。
与 RowShareLock,RowExclusiveLock,ShareLock,ShareRowExclusiveLock,ExclusiveLock 和 AccessExclusiveLock 模式冲突。
AccessExclusiveLock
被 ALTER TABLE,DROP TABLE,VACUUM 和 LOCK TABLE 语句要求。
与 RowShareLock,RowExclusiveLock,ShareLock,ShareRowExclusiveLock,ExclusiveLock 和 AccessExclusiveLock 模式冲突。
注意: 只有 AccessExclusiveLock 阻塞 SELECT (没有 FOR UPDATE)语句。
行级锁
当某行的一个内部字段被更新(或删除或标记为更新)时要求使用这些锁。Postgres 在内存里不做关于已更新的行的任何记忆,因而对锁定的行数没有任何限制,也没有锁定递增。
但是,要注意 SELECT FOR UPDATE 会更改所选定的行以标记它们,因而会导致磁盘写动作。
行级别的锁不影响数据查询。它们只是用于阻塞对同一行的写。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
锁和索引
尽管 Postgres 提供对表数据访问的非阻塞的读/写,但并非所有Postgres 里实现的索引访问模式都能够进行非阻塞读/写。
各种各样的索引类型按照下面方法操作:
GiST 和 R-Tree 索引
共享/排他的索引级锁用于读/写访问。锁在语句完成后释放。
Hash (散列)索引
共享/排他的页面级锁用于读/写访问。锁在页面处理完成后释放。
页面级锁比索引级的锁提供了更好的并行性但是容易产生死锁。
Btree
短期的共享/排他的页面级的销用于读/写访问。销在索引记录被插入/抓取后立即释放。
Btree 索引提供了无死锁条件的最高级的并行性。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
应用层的数据完整性检查
因为对 Postgres 的读动作不会锁定数据,不管事务是何隔离级别,一个事务读取的数据可能被另一个事务覆盖。换句话说,如果一条 SELECT 返回了一行,这并不意味着在返回该行时该行还存在(比如说在语句完成或事务开始后的某时)也不意味着在当前事务做提交或者回卷前该行被保护不被并行的事务删除或更新。
要保证一行的实际存在和避免其被并行更新,我们必须使用 SELECT FOR UPDATE 或者合适的 LOCK TABLE 语句。当从其他环境向 Postgres 里用可串行化模式移植应用时一定要把这些问题考虑进去。
注意: 在版本 6.5 前,Postgres 使用读动作锁,因而当从以前的 Postgres 版本向6.5(或更高版本)升级时也要考虑这些问题。
--------------------------------------------------------------------------------