--创建函数,注意此函数只能定位由于dml操作所引起的锁等待,对于ddl引起的锁等待,此sql无法完全定位
CREATE OR REPLACE FUNCTION report_lock(refcursor, refcursor)
RETURNS SETOF refcursor AS
$BODY$
declare
v_activity_count integer;
v_cur_relation_info record;
v_cur_tuple_info record;
begin
select count(distinct pl.pid) into v_activity_count from pg_stat_activity pa,pg_locks pl
where pl.pid=pa.pid and pl.pid <> pg_backend_pid();
raise notice '截止到目前有与锁相关连接数量为:%个',v_activity_count;
for v_cur_relation_info in
select pn.nspname,relname,count(*) lock_count,relation
from pg_locks pl ,pg_class pc,pg_namespace pn
where pl.relation =pc.oid and relname !~ '^pg_' and locktype='relation' and pn.oid = pc.relnamespace
group by pn.nspname,relname,relation
loop
raise notice '占有锁所在的表为%.%,与其相关的连接数量为:%个 ',v_cur_relation_info.nspname,v_cur_relation_info.relname,v_cur_relation_info.lock_count;
for v_cur_tuple_info in
select page,tuple,count(*) tuple_count from pg_locks pl
where pl.relation=v_cur_relation_info.relation and pl.locktype='tuple' and pl.page is not null and pl.tuple is not null
group by page,tuple
loop
raise notice ' 等待中更新或删除此元组的连接数量为:%个,查询元组的伪sql如下:select * from %.% where ctid=''(%,%)'';'
,v_cur_tuple_info.tuple_count,v_cur_relation_info.nspname,v_cur_relation_info.relname,v_cur_tuple_info.page,v_cur_tuple_info.tuple;
end loop;
end loop;
open $1 for
SELECT
'select pg_terminate_backend('||procpid||');' kill_prod,
procpid,
start,
now() - start AS lap,
pa.waiting,
substr(current_query,1,200) current_query
FROM
(SELECT
backendid,
pg_stat_get_backend_pid(S.backendid) AS procpid,
pg_stat_get_backend_xact_start(S.backendid) AS start,
pg_stat_get_backend_activity(S.backendid) AS current_query
FROM
(SELECT pg_stat_get_backend_idset() AS backendid) AS S
) AS S ,pg_stat_activity pa
WHERE
current_query <> '<IDLE>' and procpid<> pg_backend_pid() and pa.pid=s.procpid and pa.state<>'idle'
ORDER BY
lap DESC;
return next $1;
raise notice '##################################################################################';
raise notice '阻塞间关系阅读方式:第1列与第2列是直接阻塞关系,第1列与第2,3,4...列之间为间接阻塞关系';
raise notice '阻塞间关系阅读方式:第2列与第3列是直接阻塞关系,第2列与第3,4,5...列之间为间接阻塞关系';
open $2 for
WITH w1 AS
--查出显示的具有直接等待关系的进程
( SELECT kl.pid AS parent_pid,bl.pid AS child_pid,cast(kl.pid||'>'||bl.pid AS varchar(1000)) AS pid_rec
FROM pg_catalog.pg_locks bl --等待锁
JOIN pg_catalog.pg_locks kl --持有锁
ON kl.transactionid = bl.transactionid AND kl.pid != bl.pid WHERE NOT bl.granted and bl.mode='ShareLock' and kl.mode='ExclusiveLock')
--查出等待同一条数据的进程
,w2 as (select ARRAY_TO_STRING(ARRAY_AGG(pid),',') pid_string,ARRAY_AGG(pid) pid_array from pg_locks where locktype='tuple' group by page,tuple having count(*)>=2)
--查出显示和隐式的具有直接等待关系的进程
,w3 as (
select parent_pid,regexp_split_to_table(pid_string,',')::integer child_pid,cast(parent_pid||'>'||regexp_split_to_table(pid_string,',') AS varchar(1000)) AS pid_rec from w1,w2 where w1.child_pid = any(w2.pid_array)
union
select * from w1)
--查出所有等待关系(包括间接的)的进程
SELECT pid_rec as 阻塞间关系
--使用递归查询
FROM (WITH RECURSIVE w4 AS
( SELECT child_pid, parent_pid, pid_rec
FROM w3
UNION ALL
SELECT w3.child_pid, w3.parent_pid,cast(w4.pid_rec||'>'||w3.child_pid AS varchar(1000)) AS pid_rec
FROM w3
INNER JOIN w4 ON w3.parent_pid = w4.child_pid)
SELECT pid_rec
FROM w4) w5
ORDER BY array_length(regexp_split_to_array(pid_rec,'>'),1) DESC;
return next $2;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION report_lock(refcursor, refcursor)
OWNER TO postgres;
--查找系统中所有与锁有关连接的信息,(包括ddl语句)
SELECT
'select pg_terminate_backend('||procpid||');' kill_prod,
procpid,
start,
now() - start AS lap,
pa.waiting,
substr(current_query,1,200) current_query
FROM
(SELECT
backendid,
pg_stat_get_backend_pid(S.backendid) AS procpid,
pg_stat_get_backend_xact_start(S.backendid) AS start,
pg_stat_get_backend_activity(S.backendid) AS current_query
FROM
(SELECT pg_stat_get_backend_idset() AS backendid) AS S
) AS S ,pg_stat_activity pa
WHERE
current_query <> '<IDLE>' and procpid<> pg_backend_pid() and pa.pid=s.procpid and pa.state<>'idle'
ORDER BY
lap DESC;
--查询函数的锁信息
begin;
select report_lock('a','b');
fetch all in b;
fetch all in a;
commit;
--实际测试
--session1,执行一条更新,但不提交
postgres=# begin;
BEGIN
postgres=# update t1 set id=id+1 where id<10;
UPDATE 3
postgres=# select now();
now
-------------------------------
2016-03-25 14:55:27.924359+08
(1 row)
--session2,先执行删除,后执行更新,其被session1阻塞
postgres=# begin;
BEGIN
postgres=# delete from t1 where id=16;
DELETE 1
postgres=# update t1 set id=id+5 where id<=12;
--session3,执行更新,被session2阻塞
postgres=# begin;
BEGIN
postgres=# update t1 set id=id+1 where id=16;
--现构成阻塞链:session1阻塞session2,session2阻塞session3
--实际使用效果如下
postgres=# begin;
BEGIN
postgres=# select report_lock('a','b');
NOTICE: 截止到目前有与锁相关连接数量为:3个
NOTICE: 占有锁所在的表为public.t2,与其相关的连接数量为:3个
NOTICE: 等待中更新或删除此元组的连接数量为:1个,查询元组的伪sql如下:select * from public.t2 where ctid='(833,51)';
NOTICE: 等待中更新或删除此元组的连接数量为:1个,查询元组的伪sql如下:select * from public.t2 where ctid='(833,43)';
NOTICE: 占有锁所在的表为public.t3,与其相关的连接数量为:3个
NOTICE: 占有锁所在的表为public.t1,与其相关的连接数量为:3个
NOTICE: ##################################################################################
NOTICE: 阻塞间关系阅读方式:第1列与第2列是直接阻塞关系,第1列与第2,3,4...列之间为间接阻塞关系
NOTICE: 阻塞间关系阅读方式:第2列与第3列是直接阻塞关系,第2列与第3,4,5...列之间为间接阻塞关系
report_lock
-------------
a
b
(2 rows)
postgres=# fetch all in b;
阻塞间关系
-------------------
28121>28183>28190
28183>28190
28121>28183
(3 rows)
postgres=# fetch all in a;
kill_prod | procpid | start | lap | waiting | current_query
-------------------------------------+---------+-------------------------------+-----------------+---------+-------------------------------------
select pg_terminate_backend(28121); | 28121 | 2016-03-25 14:55:27.924359+08 | 00:01:54.639125 | f | select now();
select pg_terminate_backend(28183); | 28183 | 2016-03-25 14:55:34.451774+08 | 00:01:48.11171 | t | update t1 set id=id+5 where id<=12;
select pg_terminate_backend(28190); | 28190 | 2016-03-25 14:56:05.956339+08 | 00:01:16.607145 | t | update t1 set id=id+1 where id=16;
(3 rows)
postgres=# commit;
COMMIT
通过函数定位DML锁等待
最新推荐文章于 2020-12-05 07:17:49 发布