[翻译]PostgreSQL中的死锁
Contents
在讨论死锁之前,让我们看一下锁的类型以及它们在PostgreSQL中的获取方法。
锁的类型: * 表级锁 以及 * 行级锁
表级锁:
AcessShareLock(访问共享锁):通过在一张表或多张表一条
SELECT
语句检索数据时就会自动获取。这个模式会阻塞在同一张表下的ALTER TABLE
,DROP TABLE
以及VACUUM (AccessExclusiveLock,访问排他锁)
操作。RowShareLock (行共享锁):通过一条
SELECT...FOR UPDATE
子句自动获取。它会在同一张表上阻塞并发的ExclusiveLock
(排他锁)以及AccessExclusiveLock
(访问排他锁)。RowExclusiveLock (行排他锁):通过
UPDATE
,INSERT
,或者DELETE
命令自动获取。它会在同一张表上阻塞ALTER TABLE
,DROP TABLE
,VACUUM
和CREATE INDEX
命令。(ShareLock[共享锁],ShareRowExclusiveLock[共享行排他锁],ExclusiveLock[排他锁],AccessExclusiveLock[访问排他锁])。ShareLock(共享锁): 通过
CREATE INDEX
命令自动获取。它会在同一张表上阻塞INSERT
,UPDATE
,DELETE
,ALTER TABLE
,DROP TABLE
, 以及VACUUM
命令.(RowExclusiveLock[行排他锁], ShareRowExclusiveLock[共享行排他锁], ExclusiveLock[排他锁], 以及 AccessExclusiveLock[访问排他锁])ShareRowExclusiveLock(共享行排他锁):这个锁模式与ExclusiveLock(排他锁)是一样的,但是它允许获取并发的 RowShareLock(行共享锁)
ExclusiveLock(排他锁):”每个事务在它的事务ID的整个时间里都会持有一个Exclusive Lock(排他锁)”。如果一个事务发现它需要特别地等待另一个事务,它就会尝试地在另一个事务ID上获取 Share Lock(共享锁)。这仅当另一个事务结束并释放它自己的锁时才会成功。(注意,冲突)。Exclusive Lock(排他锁)会在同一张表上阻塞
INSERT
,UPDATE
,DELETE
,CREATE INDEX
,ALTER TABLE
,DROP TABLE
,SELECT...FOR UPDATE
以及VACUUM
命令。AccessExclusiveLock(访问排他锁):通过
ALTER TABLE
,DROP TABLE
, 或者VACUUM
命令来修改表时自动获取。从开始在表上获取锁时,会阻塞任何并发命令或者其他锁模式。
行级锁:
行级锁有两种类型:共享锁和排他锁。不要混淆锁的命名,你可以通过在视图pg_locks
中的列lock_type
来区分是表级锁,还是行级锁。
Exclusive lock(排他锁):当通过
UPDATE
或DELETE
命中行时就会自动获取该锁。锁会一直被持有,直到一个事务提交或回滚了。为了手动获取exclusive-lock
(排他锁),可以使用SELECT FOR UPDATE
。Share-Lock(共享锁):当通过
SELECT...FOR SHARE
命中行时就会自动获取该锁。
注意:在这两种情况下的行级锁,数据检索是一点也不会影响的。行级锁会阻塞“写”(即,“写”会阻塞“写”)。
死锁:
现在到死锁了,你已经知道锁的模式以及获取这些锁的方法,有些情况下事务会陷入死锁中。我相信应用程序设计是导致死锁的罪魁祸首。死锁大多数是由于ExclusiveLock(排他锁)例如UPDATE
或DELETE
操作导致的。
##什么是死锁?
进程A持有对象X的锁,并且正等待对象Y的锁。进程B持有对象Y的锁,并且正等待对象X的锁。在这时,这两个进程就会进入所谓的“死锁”,每个进程都想获取另一个进程持有的锁。在这个状态下,他们都在永远等待对方释放锁。他们之一必须放弃并释放自己拥有的锁。现在,死锁检测器就会检测到并且允许一个进程成功提交事务,而另一个则进行回滚。
为了解决死锁,要用这样一个方法来设计应用程序——任何事务“UPDATE”或“DELETE”都应该成功地取得表的所有权。通过SHARE UPDATE EXCLUSIVE 模式
或者SELECT...FOR UPDATE
,又或者ACCESS EXCLUSIVE 模式
来锁表并且完成事务。在这个模型下,死锁检测器永远不会抛出它已经检测到一个EXCLUSIVE LOCK(排他锁)。
你可通过这个办法来解决上面的图的情况,你会看到死锁检测器永远不会抛出错误。
锁查询语句
\set locks 'SELECT w.locktype AS waiting_locktype,w.relation::regclass AS waiting_table,w.transactionid, substr(w_stm.current_query,1,20) AS waiting_query,w.mode AS waiting_mode,w.pid AS waiting_pid,other.locktype AS other_locktype,other.relation::regclass AS other_table,other_stm.current_query AS other_query,other.mode AS other_mode,other.pid AS other_pid,other.granted AS other_granted FROM pg_catalog.pg_locks AS w JOIN pg_catalog.pg_stat_activity AS w_stm ON (w_stm.procpid = w.pid) JOIN pg_catalog.pg_locks AS other ON ((w.\"database\" = other.\"database\" AND w.relation = other.relation) OR w.transactionid = other.transactionid) JOIN pg_catalog.pg_stat_activity AS other_stm ON (other_stm.procpid = other.pid) WHERE NOT w.granted AND w.pid <> other.pid;;'
关于锁的信息链接
http://www.postgresql.org/docs/9.0/static/sql-lock.html http://developer.postgresql.org/pgdocs/postgres/explicit-locking.html
希望你有一个关于PostgreSQL锁的概念了。 希望在之后的博文中可以再次见到你。:)
–Raghav
By Raghavendra
我附: 现在的PG有8种表级锁了,除了上面作者提到的7种,还有一种是:
- SHARE UPDATE EXCLUSIVE 锁
与”Share update exclusive,Share,Share row ,exclusive,exclusive,Access exclusive”模式冲突,这种模式保护一张表不被并发的模式更改和VACUUM;
“Vacuum(without full), Analyze ”和 “Create index concurrently”命令会获得这种类型锁。
资料:
http://www.postgresql.org/docs/9.0/static/explicit-locking.html