PG从小工到专家学习笔记-事务、并发、锁,详情请参考文档:PG从小工到专家学习笔记-事务、并发、锁.pdf
事务、并发、锁
锁
表级锁
-
表级锁模式
- ACCESS SHARE - ROW SHARE - 只与ACCESS EXCLUSlVE 模式冲突 。
SELECT命令将在所引用的表上加此类型的锁。通常,任何只读取表而不修改表的查询都会请求这种锁模式
- 与EXCLUSIVE和ACCESS EXCLUSIVE锁模式冲突。
SELECT FOR UPDATE 和 SELECT FOR SHARE命令会在目标表上加此类型的锁
-
- 与 SHARE、SHARE ROW EXCLUSIVE、 EXCLUSIVE、 ACCESS EXCLUSIVE 锁模式冲突 。UPDATE、DELETE、INSERT命令会自动在所修改的表上请求加上这个锁。 通常,修改表中数据的命令都是加这种锁
- ROW EXCLUSIVE
-
- SHARE UPDATE EXCLUSIVE
- 与 SHARE UPDATE EXCLUSIVE、 SHARE、 SHARE ROW EXCLUSIVE、 EXCLUSIVE、 ACCESS EXCLUSIVE锁模式冲突。 在模式改变和运行 VARCUM 并发的情况下.这个模式保护一个表 。VACUM (不带 FULL选项 )、 ANALYZE、 CREATE lNDEX CONCURR-ENTLY 命令请求这样的锁
-
- SHARE
- 与 ROW EXCLUSIVE、 SHARE UPDATE EXCLUSIVE、 SHARE ROW EXCLUSIVE、 EXCLUSIVE、 ACCESS EXCLUSIVE 锁模式冲突 。 这个模式可避并发数据修改 。CREATE INDEX (不带CONCURRENTLY选项)语句要求这样的锁模式
-
- SHARE ROW EXCLUSIVE
- 与 ROW EXCLUSIVE、 SHARE UPDATE EXCLUSIVE、 SHARE、 SHARE ROW EXCLUSIVE、 EXCLUSIVE、 ACCESS EXCLUSIVE锁模式冲突。
任何 PostgreSQL命令都不会自动请求这个锁模式
-
- EXCLUSIVE
- 与 ROW SHARE、 ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、 SHARE、 SHARE ROW EXCLUSIVE、 EXCLUSIVE、 ACCESS EXCLUSIVE锁模式冲突。 这个模式只允许并发ACCESS SHARE锁。 也就是说,只有对表的读动作可以和持有这个锁的事务并发执行 。
任何 PostgreSQL命令都不会在用户表上自动请求这个锁模式。 不过,在执行某些操作时,会在某些系统表上请求这个锁
-
- ACCESS EXCLUSIVE
- 与所有模式冲突 (包括 ACESSSHARE、 ROWSHARE、 ROWEXCLUSIVE, SHARE UPDATE EXCLUSIVE 、 SHARE 、 SHARE ROW EXCLUSIVE 、 EXCLUSIVE 、 ACCESS EXCLUSIVE, 等等)。
这个模式保证只能有一个人访问此表。
ALTER TABLE、 DROP TABLE、 TRUNCATE、 REINDEX、 CLUSTER、
VACUUM FULL命令要求这样的锁。 在 LOCK TABLE命令中没有明确声明需要的锁模式时,它是默认锁模式
-
表解锁冲突矩阵
-
意向锁及表锁冲突矩阵
- mysql 意向锁冲突矩阵比对
行级锁
- 共享锁:读锁(MVCC读又可能不加锁,但习惯性共享锁叫做读锁)
- 排它锁:写锁
死锁及防范
- 处理: PostgreSQL 能够自动侦测到死锁,然后会退出其中一个事务,从而 允许其他事务完成。
- 死锁检测原理:
LOCK TABLE
-
语法:LOCK [ TABLE ] [ ONLY ] name [, …] [ IN lockmode MODE ) [ NOWAIT ]
说明如下 。
name:表名。
lockmode :就是前面介绍的几种表级锁模式,即 ACCESS SHARE、 ROW SHARE、 ROW EXCLUSIVE 、 SHARE UPDATE EXCLUSIVE 、 SHARE 、SHARE ROW EXCLUSIVE、 EXCLUSIVE、 ACCESS EXCLUSIVE。
NOWAIT :如果没有 NOWAIT这个关键字时, 当无法获得锁时,会一直等待,而如果加了NOWAIT关键字,在无法立即获取该锁时,此命令会立即退出并且发出一个错误信息 。
行级锁
-
语法:SELECT … FOR ( UPDATE I SHARE I [ OF’ table name [I •••J J [ NOWAIT l […] )
“ NOWAIT” 关键字与“ LOCK TABLE”中的相同,加了“ NOWAIT”后, 若无法获得锁,则 直接报错 ,而不会一直等待 。
如果在FORUPDATE或FORSHARE中使用“OFtable name” 明确指定了表名字,那 么只有这些被指定的表会被锁定,其他在 SELECT 中使用的表将不会被锁定 。
一个其后不 带“OFtable_name,,的FORUPDATE或FORSHARE子句将锁定该命令中所有使用的表
如 果 FOR UPDATE 或 FOR SHARE 是应用 于一个视图或者子查闹的 , 那么它会锁定该视图或子 查询中使用到的所有表。
主查询中引用了WITH查询时, WITH查询中的表并不会被锁定 。 如果想锁定 WITH 查询内的表行,需要在 WITH 查询内指定 FOR UPDATE 或 FOR SHARE 关键字。
锁的查看
- pg_locks视图查看锁信息,pg_locks中对每一个活动可锁对象、请求锁模式和相关事务的组合都有一行。因此,如果多个事务持有或者正在等待一个可锁对象上的锁,同一个可锁对象可能出现很多次。但是,一个当前没有被锁的对象根本不会出现。
一个行的granted为真表示一个被指定事务持有的锁。为假表示该事务当前正在等待获取这个锁,这意味着某个其他事务正持有同一个可锁对象上的一个冲突锁。等待中的事务将一直休眠直到其他锁被释放(或者一个死锁状态被检测到)。一个单一事务在同一时间只能等待最多一个锁。
每个事务在其生存周期内都持有一个在其虚拟事务ID上的排他锁。如果一个永久ID被分配给事务(通常发生在事务改变数据库状态时),它也会持有一个在其永久事务ID上的排他锁直到它结束。当一个事务发现它需要等待另一个事务,它也会尝试获取其他事务ID上的共享锁(不管是虚拟还是永久ID,视情况而定)。这只有当其他进程终止并释放其锁后才会成功。
尽管元组是一种可锁对象,关于行级锁的信息被存储在磁盘而不是内存中,因此行级锁通常不在这个视图中出现。如果一个事务正在等待一个行级锁,它通常在这个视图中出现,并且表示形式为正在等待已持有该行级锁的永久事务ID上的锁。
pg_locks提供了一个对于整个数据集簇中所有锁的全局视图,而不仅仅是与当前数据库相关的锁。尽管它的relation列可以被连接到pg_class.oid来标识被锁关系,但这种方法只有在关系属于当前数据库(database列是当前数据库OID或者0的锁对应的关系)的情况下才会得到正确的结果。
-
持有或等待持有每一个锁的会话的信息
SELECT * FROM pg_locks pl LEFT JOIN pg_stat_activity psa
ON pl.pid = psa.pid; -
查询持有该锁的预备事务的信息(一个预备事务不可能正在等待一个锁,但它在运行中会一直持有已获得的锁)
SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
ON pl.virtualtransaction = ‘-1/’ || ppx.transaction; -
数据来源说明
pg_locks视图显示来自于普通锁管理器和谓词锁管理器的数据,它们是独立的系统。此外,普通锁管理器把它的锁分为普通锁和快速路径锁。这些数据并不被保证是完全一致的。当视图被查询时,快速路径锁上的数据(fastpath = true)会被一次性从每一个后端收集起来,且并不冻结整个锁管理器的状态。因此有可能某些锁在上述信息被收集的过程中被获得或者释放。注意,不管怎样这些锁是已知不会和任何当前正在发生的锁冲突。在所有后端已经查询了快速路径锁后,普通锁管理器的剩余部分被作为一个单元锁住,并且所有剩余锁的一个一致快照被作为一个原子动作收集。在解锁普通锁管理器后,谓词锁管理器也被类似地锁住并且所有谓词锁被作为一个原子动作收集。因此,在快速路径锁这种特殊情况下,每一个锁管理器会传递一个一致的结果组。但由于我们并不会同时锁上两个锁管理器, 在我们询问完普通锁管理器后或者询问谓词锁管理器之前,锁可以被获得或者释放。
-
注意:如果对此视图频繁地访问,对普通或者谓词锁管理器加锁可能会对数据库性能产生一定影响。虽然这些锁只会在最少的时间内被保持(足以从锁管理器获得数据),但这无法完全消除可能产生的性能影响。
DDL事务
特性
大多数 DDL可以包含在一个事务中,而且是可以回滚
使用
- AUTOCOMMIT自动开启
- set AUTOCOMMIT off
- 查看:\echo :AUTOCOMMIT
- 显示begin开启
SAVEPOINT
- savepoint my savepointOl;
- rollback to SAVEPOINT my_savepointOl;
隔离级别
-
READ UNCOMMITTED: 读未提交
- 问题:脏读、不可重复读、幻读
-
READ COMMITTED: 读已提交(默认)
SELECT 查询看到的是在查询开始运行瞬 间的一个快照;
注意,在同一个事务里两个相邻的 SELECT命令可能看到不同的快照,因 为可能有其他事务会在第一个 SELECT执行期间提交。- 问题:不可重复读、幻读
-
REPEATABLE READ: 重复读
- 问题:幻读
-
SERIALIZABLE: 串行化
两阶段提交
-
调用操作,不提交事务
应用程序先调用各台数据库做一些操作,但不提交事务 ;然后调用事务协调器(这个
协调器可能也是由应用自己实现的)中的提交方法。 -
PREPARE TRANSACTION
事务协调器将联络事务中涉及 的每台数据库,并通知它们准备提交事务 ,这是第一阶段的开始。
在 PostgreSQL一般是调用“PREPARETRANSACTION”命令。 -
已准备好提交的信息写人持久存储区中
各台数据库接收到“ PREPARE TRAlNSACTION”命令后,如果要返回成功则,数据库必须将自己置于以下状态 : 确保后续能在被要求提交事务时提交事务,或者在被要求回滚 事务时能回滚事务。
所以PostgreSQL会将已准备好提交的信息写人持久存储区中。
如果数据库无法完成此事务,它会直接返回失败给事务协调器 。 -
事务协调器接收到所有数据库的响应
-
第二阶段:commit/rollback
如果任一数据库在第一阶段返回失败, 则事务协调器将会发一个回烧命令( ROLLBACK PREPARED)给各台数据库。
如果所有数据库的响应都是成功的,则向各台 数据库发送“ COMMIT PREPARED”命令 ,通知各台数据库事务成功。




