postgres中的锁定顺序SELECT FOR UPDATE

Hep*_*tic 7 postgresql

假设这两个表a,并b有单排,查询

 SELECT * FROM a, b FOR UPDATE
Run Code Online (Sandbox Code Playgroud)

应该得到两个行级锁(一个在a上,一个在b上).是否有任何已定义的锁定获取顺序?并且有没有办法要求表b中的锁从a之前得到锁定(以避免与其他事务的死锁)?

Cra*_*ger 5

是否有任何已定义的锁定获取顺序?

SELECT *无论如何,这不是我所知道的.由于没有记录此情况下的锁定顺序,即使在实践中存在,也不能依赖它.它可能会在未来版本中发生变化.

并且有没有办法要求表b中的锁从a之前得到锁定(以避免与其他事务的死锁)?

如果你必须使用SELECT *那么没有.但是,如果你可以控制SELECT-list,是的.看起来行锁是按照相关元组字段出现在SELECT列表中的顺序获取的,因此:

SELECT a.x, b.x FROM b, a FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)

a从那时起在该行上获取一个锁b.目前,无论如何; 我不认为标准指定了这个,并且在文档中没有看到它的任何引用,所以这可能会在以后发生变化.

就个人而言......我会使用一个DO块或单独的查询.有可能使用一些子查询或CTE来完成它,但您必须在它们之间创建某种形式的人工依赖以确保排序.脆弱而不值得.


让我们看看实际发生了什么:

regress=> EXPLAIN (VERBOSE) SELECT * FROM a, b FOR UPDATE;
                                  QUERY PLAN                                   
-------------------------------------------------------------------------------
 LockRows  (cost=0.00..129674.00 rows=5760000 width=20)
   Output: a.x, b.x, a.ctid, b.ctid
   ->  Nested Loop  (cost=0.00..72074.00 rows=5760000 width=20)
         Output: a.x, b.x, a.ctid, b.ctid
         ->  Seq Scan on public.a  (cost=0.00..34.00 rows=2400 width=10)
               Output: a.x, a.ctid
         ->  Materialize  (cost=0.00..46.00 rows=2400 width=10)
               Output: b.x, b.ctid
               ->  Seq Scan on public.b  (cost=0.00..34.00 rows=2400 width=10)
                     Output: b.x, b.ctid
(10 rows)
Run Code Online (Sandbox Code Playgroud)

执行查询,然后将结果输入LockRows节点.怎么LockRows办?为此,是时候进行源代码潜水了.

ExecLockRowsinsrc/backend/executor/nodeLockRows.c是相关代码.那里有很多,但它的要点是它按顺序迭代RowMarks 列表并按顺序获取每个锁.该列表由以下内容设置:ExecInitLockRows复制并过滤在规划期间准备并存储在LockRows节点中的列表.

我没有时间追溯到规划器中找到LockRows创建的顺序,但是IIRC它基本上只是解析顺序(for SELECT *)或字段在SELECT列表中出现的顺序(如果你没有使用*).我建议不要依赖它.