BUG #16820: PG will have a deadlock when multiple rows are updated concurrently

From: PG Bug reporting form <noreply(at)postgresql(dot)org>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Cc: abcxiaod(at)126(dot)com
Subject: BUG #16820: PG will have a deadlock when multiple rows are updated concurrently
Date: 2021-01-13 07:43:43
Message-ID: 16820-2f08e0bd1687ee34@postgresql.org
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

The following bug has been logged on the website:

Bug reference: 16820
Logged by: yi Ding
Email address: abcxiaod(at)126(dot)com
PostgreSQL version: 10.13
Operating system: linux
Description:

The test is as follows:
1、the test table:
postgres=# selet *from zxin_cardcapacity;
a | b | usecapacity
----+-----+---------------
1 | 1 | 99998933863
2 | 1 | 99960281190
3 | 1 | 99960808567
4 | 1 | 9999836457
5 | 1 | 9999836457
6 | 1 | 9999836457
7 | 1 | 9999836457
8 | 1 | 9999836457
9 | 1 | 9999836457
10 | 1 | 9999836457
11 | 1 | 9999836457
12 | 1 | 9999836457
13 | 1 | 9999836457
14 | 1 | 9999836457
15 | 1 | 9999836457

2、Pressure measurement tools:
Use pgbench to initiate 100 concurrent:
$pgbench -c 100 -T 180000 -rf update.sql postgres

3. Test results:
(1) As long as the data of a single update exceeds 1 row, a deadlock will
occur:
update zxin_cardcapacity set usecapacity = usecapacity - 2 where a >2;
Pg_locks related records during deadlock:
locktype | database |relatio |page|tuple|virtualxid|transactionid|classid |
objid | objsubid | virtualtransaction | pid | mode | granted |
fastpath
transactionid | | | | | |
546872 | | | | 23/1480 |
166197 | ExclusiveLock | t | f
transactionid | | | | | |
546875 | | | | 23/1480 |
166197 | ShareLock | f | f
transactionid | | | | | |
546872 | | | | 20/1596 |
166189 | ShareLock | f | f
transactionid | | | | | |
546875 | | | | 20/1596 |
166189 | ExclusiveLock | t | f

Deadlock data:
postgres=# select xmax,xmin,* from zxin_cardcapacity ;
xmax | xmin | a | b | usecapacity
--------+--------+---+---+-------------
546875 | 546867 | 3 | 1 | 99999602775
546872 | 546867 | 2 | 1 | 99999075398

(2) Update one row first, then update multiple rows, there will be no
deadlock:
do $$
begin
update zxin_cardcapacity set usecapacity = usecapacity - 2 where a = 1;
update zxin_cardcapacity set usecapacity = usecapacity - 2 where a >=2;
end $$;

(3) If the entire table is updated without sorting, a deadlock will occur:
do $$
declare
v_1 record;
begin
for v_1 in select a from zxin_cardcapacity
loop
update zxin_cardcapacity set usecapacity = usecapacity - 2 where a =
v_1.a;
end loop;
end $$;

(4) After sorting, the entire table is updated cyclically without
deadlock:
do $$
declare
v_1 record;
begin
for v_1 in select a from zxin_cardcapacity order by a
loop
update zxin_cardcapacity set usecapacity = usecapacity - 2 where a =
v_1.a;
end loop;
end $$;

After analysis, we believe that the deadlock problem is caused by PG's
unique MVCC and locking mechanism.
1. Whenever a data row is updated in PG, the physical location of the row
changes, resulting in high concurrency scenarios, each query or update
operation, the order of the returned data rows is different.
2. When PG is updating data, in order to increase efficiency and realize
multi-session parallel update, the data rows in the table are locked row by
row.
The combination of the above two reasons leads to a deadlock when multiple
rows of data are updated concurrently.

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message mayur 2021-01-13 08:35:31 Re: BUG #16812: Logical decoding error
Previous Message Michael Paquier 2021-01-13 05:53:58 Re: BUG #16577: Segfault on altering a table located in a dropped tablespace