PostgreSQL为开发者提供了一组丰富的工具来管理对数据的并发访问。在内部,数据一致性通过使用一种多版本模型(多版本并发控制,MVCC)来维护。这就意味着每个 SQL 语句看到的都只是一小段时间之前的数据快照(一个数据库版本),而不管底层数据的当前状态。这样可以保护语句不会看到可能由其他在相同数据行上执行更新的并发事务造成的不一致数据,为每一个数据库会话提供事务隔离。MVCC避免了传统的数据库系统的锁定方法,将锁争夺最小化来允许多用户环境中的合理性能。
使用MVCC并发控制模型而不是锁定的主要优点是在MVCC中,对查询(读)数据的锁请求与写数据的锁请求不冲突,所以读不会阻塞写,而写也从不阻塞读。甚至在通过使用革新的可序列化快照隔离(SSI)级别提供最严格的事务隔离级别时,PostgreSQL也维持这个保证。
隔离级别 | 脏读 | 不可重复读 | 幻读 | 序列化异常 |
---|---|---|---|---|
读未提交 | 允许,但不在 PG 中 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 允许,但不在 PG 中 | 可能 |
可序列化 | 不可能 | 不可能 | 不可能 | 不可能 |
读已提交是PostgreSQL中的默认隔离级别。 当一个事务运行使用这个隔离级别时, 一个查询(没有FOR UPDATE/SHARE
子句)只能看到查询开始之前已经被提交的数据, 而无法看到未提交的数据或在查询执行期间其它事务提交的数据。实际上,SELECT
查询看到的是一个在查询开始运行的瞬间该数据库的一个快照。不过SELECT
可以看见在它自身事务中之前执行的更新的效果,即使它们还没有被提交。还要注意的是,即使在同一个事务里两个相邻的SELECT
命令可能看到不同的数据, 因为其它事务可能会在第一个SELECT
开始和第二个SELECT
开始之间提交。
可重复读隔离级别只看到在事务开始之前被提交的数据;它从来看不到未提交的数据或者并行事务在本事务执行期间提交的修改(不过,查询能够看见在它的事务中之前执行的更新,即使它们还没有被提交)。
这个级别与读已提交不同之处在于,一个可重复读事务中的查询可以看见在事务中第一个非事务控制语句开始时的一个快照,而不是事务中当前语句开始时的快照。因此,在一个单一事务中的后续SELECT
命令看到的是相同的数据,即它们看不到其他事务在本事务启动后提交的修改。
可序列化隔离级别提供了最严格的事务隔离。这个级别为所有已提交事务模拟序列事务执行;就好像事务被按照序列一个接着另一个被执行,而不是并行地被执行。
PostgreSQL提供了多种锁模式用于控制对表中数据的并发访问。 这些模式可以用于在MVCC无法给出期望行为的情境中由应用控制的锁。
视图pg_locks
提供了数据库服务器上活动进程中保持的锁的信息。
名称 | 类型 | 引用 | 描述 |
---|---|---|---|
locktype |
text |
可锁对象的类型: relation , extend , page , tuple , transactionid , virtualxid , object , userlock 或 advisory |
|
database |
oid |
|
锁目标存在的数据库的OID,如果目标是一个共享对象则为0,如果目标是一个事务ID则为空 |
relation |
oid |
|
作为锁目标的关系的OID,如果目标不是一个关系或者只是关系的一部分则此列为空 |
page |
integer |
作为锁目标的页在关系中的页号,如果目标不是一个关系页或元组则此列为空 | |
tuple |
smallint |
作为锁目标的元组在页中的元组号,如果目标不是一个元组则此列为空 | |
virtualxid |
text |
作为锁目标的事务虚拟ID,如果目标不是一个虚拟事务ID则此列为空 | |
transactionid |
xid |
作为锁目标的事务ID,如果目标不是一个事务ID则此列为空ID | |
classid |
oid |
|
包含锁目标的系统目录的OID,如果目标不是一个普通数据库对象则此列为空 |
objid |
oid |
任意OID列 | 锁目标在它的系统目录中的OID,如果目标不是一个普通数据库对象则为空 |
objsubid |
smallint |
锁的目标列号(classid 和objid 指表本身),如果目标是某种其他普通数据库对象则此列为0,如果目标不是一个普通数据库对象则此列为空 |
|
virtualtransaction |
text |
保持这个锁或者正在等待这个锁的事务的虚拟ID | |
pid |
integer |
保持这个锁或者正在等待这个锁的服务器进程的PID,如果此锁被一个预备事务所持有则此列为空 | |
mode |
text |
此进程已持有或者希望持有的锁模式 | |
granted |
boolean |
如果锁已授予则为真,如果锁被等待则为假 | |
fastpath |
boolean |
如果锁通过快速路径获得则为真,通过主锁表获得则为假 |
冲突的锁模式:
请求的锁模式 | 当前的锁模式 | |||||||
---|---|---|---|---|---|---|---|---|
ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE | |
ACCESS SHARE | X | |||||||
ROW SHARE | X | X | ||||||
ROW EXCLUSIVE | X | X | X | X | ||||
SHARE UPDATE EXCLUSIVE | X | X | X | X | X | |||
SHARE | X | X | X | X | X | |||
SHARE ROW EXCLUSIVE | X | X | X | X | X | X | ||
EXCLUSIVE | X | X | X | X | X | X | X | |
ACCESS EXCLUSIVE | X | X | X | X | X | X | X | X |
冲突的行级锁
要求的锁模式 | 当前的锁模式 | |||
---|---|---|---|---|
FOR KEY SHARE | FOR SHARE | FOR NO KEY UPDATE | FOR UPDATE | |
FOR KEY SHARE | X | |||
FOR SHARE | X | X | ||
FOR NO KEY UPDATE | X | X | X | |
FOR UPDATE | X | X | X | X |
原文:https://www.cnblogs.com/ryjJava/p/14364909.html