PostgreSQL:"deadlock detected" 错误的可能原因
PostgreSQL: Possible causes for "deadlock detected" error
我们目前在以下触发器上遇到死锁,该触发器同时被多个线程调用(不知道如何给出确切的线程数)。
任何提示,无论是发现锁定原因,还是解决问题,都将不胜感激。-
这是在 RHEL 6.4 上的 Enterprise DB 9.2.1.3 (PostgreSQL) 上 运行
当前代码:
CREATE OR replace FUNCTION reports.update_cobertura
() returns TRIGGER
AS
$body$declare has_signal BOOLEAN;
ip inet;
id_proveedor INTEGER;
mejora BOOLEAN;
thislat NUMERIC;
thislng NUMERIC;
BEGIN
has_signal := NEW.tipo='R';
SELECT avl_instantaneo.ip
INTO ip
FROM avl_instantaneo
WHERE imei=NEW.imei
AND avl_instantaneo.ip<>'';
IF ip IS NOT NULL THEN
SELECT reports.proveedor_red.id_proveedor
INTO id_proveedor
FROM reports.proveedor_red
WHERE ip<<= red;
IF id_proveedor IS NOT NULL THEN
FOR decimales IN 0..4
LOOP
BEGIN
thislat := round(NEW.latitud:: NUMERIC,decimales);
thislng := round(NEW.longitud::NUMERIC,decimales);
IF has_signal THEN
INSERT INTO reports.cobertura
(
lat,
lng,
ONLINE,
id_provider,
zoom
)
VALUES
(
thislat,
thislng,
1,
id_proveedor,
decimales
);
ELSE
INSERT INTO reports.cobertura
(
lat,
lng,
OFFLINE,
id_provider,
zoom
)
VALUES
(
thislat,
thislng,
1,
id_proveedor,
decimales
);
END IF;
EXCEPTION
WHEN integrity_constraint_violation THEN
BEGIN
thislat := round(NEW.latitud:: NUMERIC,decimales);
thislng := round(NEW.longitud::NUMERIC,decimales);
IF has_signal THEN
UPDATE reports.cobertura
SET ONLINE=ONLINE+1
WHERE id_provider=id_proveedor
AND lat=thislat
AND lng=thislng
AND zoom=decimales;
ELSE
UPDATE reports.cobertura
SET OFFLINE=OFFLINE+1
WHERE id_provider=id_proveedor
AND lat=thislat
AND lng=thislng
AND zoom=decimales;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE warning 'unknown exception on update: % / %',SQLERRM, SQLSTATE;
END;
WHEN restrict_violation THEN
RAISE warning 'restrict_violation: % / %',SQLERRM, SQLSTATE;
WHEN unique_violation THEN
RAISE warning 'unique_violation: % / %',SQLERRM, SQLSTATE;
WHEN check_violation THEN
RAISE warning 'check_violation: % / %',SQLERRM, SQLSTATE;
WHEN exclusion_violation THEN
RAISE warning 'exclusion_violation: % / %',SQLERRM, SQLSTATE;
WHEN deadlock_detected THEN
RAISE warning 'deadlock_detected: % / %',SQLERRM, SQLSTATE;
--raise;
WHEN OTHERS THEN
RAISE warning 'unknown exception: % / %',SQLERRM, SQLSTATE;
END;
END LOOP;
END IF;
END IF;
RETURN NEW;
END;
$body$ LANGUAGE plpgsql volatile cost 100;
引用table:
CREATE TABLE reports.cobertura
(
lat DOUBLE PRECISION NOT NULL,
lng DOUBLE PRECISION NOT NULL,
id_provider INTEGER NOT NULL,
zoom INTEGER NOT NULL,
online BIGINT DEFAULT 0,
OFFLINE BIGINT DEFAULT 0,
CONSTRAINT cobertura_pkey PRIMARY KEY (lat, lng, id_provider),
CONSTRAINT cobertura_id_provider_fkey FOREIGN KEY (id_provider) REFERENCES reports.proveedor_movil (id_proveedor) match simple ON
UPDATE no action
ON
DELETE no action
)
WITH
(
oids=FALSE
);
-- Index: reports.cobertura_update
-- DROP INDEX reports.cobertura_update;CREATE INDEX cobertura_update
ON reports.cobertura
using btree
(
lat,
lng,
id_provider,
zoom
);
您可能会发现此查询对于识别死锁事务很有用:
with locks as (
select
pg_stat_activity.datname, application_name,
substr(pg_class.relname, 1, 32) relname, page, tuple,
pg_locks.transactionid as txid,
pg_locks.virtualtransaction as vtxid,
locktype, pg_locks.mode, pg_locks.granted, pg_stat_activity.usename,
substr(pg_stat_activity.query, 1, 255), pg_stat_activity.query_start,
age(now(), pg_stat_activity.query_start) as "age",
pg_stat_activity.pid -- , pg_locks.*
from pg_stat_activity, pg_locks
left outer join pg_class on (pg_locks.relation=pg_class.oid)
where
pg_locks.pid=pg_stat_activity.pid
and pg_stat_activity.pid != pg_backend_pid()
and (not granted OR pg_locks.mode like '%Exclusive%')
order by query_start
)
select * from locks a where not granted
or exists (select * from locks b where not b.granted
and COALESCE(a.relname, '')=COALESCE(b.relname, '')
and COALESCE(a.page, 0)=COALESCE(b.page, 0)
and COALESCE(a.tuple, 0)=COALESCE(b.tuple, 0)
and COALESCE(a.txid, '0'::xid)=COALESCE(b.txid, '0'::xid)
-- and COALESCE(a.vtxid, '')=COALESCE(b.vtxid, '')
);
我们目前在以下触发器上遇到死锁,该触发器同时被多个线程调用(不知道如何给出确切的线程数)。
任何提示,无论是发现锁定原因,还是解决问题,都将不胜感激。-
这是在 RHEL 6.4 上的 Enterprise DB 9.2.1.3 (PostgreSQL) 上 运行
当前代码:
CREATE OR replace FUNCTION reports.update_cobertura
() returns TRIGGER
AS
$body$declare has_signal BOOLEAN;
ip inet;
id_proveedor INTEGER;
mejora BOOLEAN;
thislat NUMERIC;
thislng NUMERIC;
BEGIN
has_signal := NEW.tipo='R';
SELECT avl_instantaneo.ip
INTO ip
FROM avl_instantaneo
WHERE imei=NEW.imei
AND avl_instantaneo.ip<>'';
IF ip IS NOT NULL THEN
SELECT reports.proveedor_red.id_proveedor
INTO id_proveedor
FROM reports.proveedor_red
WHERE ip<<= red;
IF id_proveedor IS NOT NULL THEN
FOR decimales IN 0..4
LOOP
BEGIN
thislat := round(NEW.latitud:: NUMERIC,decimales);
thislng := round(NEW.longitud::NUMERIC,decimales);
IF has_signal THEN
INSERT INTO reports.cobertura
(
lat,
lng,
ONLINE,
id_provider,
zoom
)
VALUES
(
thislat,
thislng,
1,
id_proveedor,
decimales
);
ELSE
INSERT INTO reports.cobertura
(
lat,
lng,
OFFLINE,
id_provider,
zoom
)
VALUES
(
thislat,
thislng,
1,
id_proveedor,
decimales
);
END IF;
EXCEPTION
WHEN integrity_constraint_violation THEN
BEGIN
thislat := round(NEW.latitud:: NUMERIC,decimales);
thislng := round(NEW.longitud::NUMERIC,decimales);
IF has_signal THEN
UPDATE reports.cobertura
SET ONLINE=ONLINE+1
WHERE id_provider=id_proveedor
AND lat=thislat
AND lng=thislng
AND zoom=decimales;
ELSE
UPDATE reports.cobertura
SET OFFLINE=OFFLINE+1
WHERE id_provider=id_proveedor
AND lat=thislat
AND lng=thislng
AND zoom=decimales;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE warning 'unknown exception on update: % / %',SQLERRM, SQLSTATE;
END;
WHEN restrict_violation THEN
RAISE warning 'restrict_violation: % / %',SQLERRM, SQLSTATE;
WHEN unique_violation THEN
RAISE warning 'unique_violation: % / %',SQLERRM, SQLSTATE;
WHEN check_violation THEN
RAISE warning 'check_violation: % / %',SQLERRM, SQLSTATE;
WHEN exclusion_violation THEN
RAISE warning 'exclusion_violation: % / %',SQLERRM, SQLSTATE;
WHEN deadlock_detected THEN
RAISE warning 'deadlock_detected: % / %',SQLERRM, SQLSTATE;
--raise;
WHEN OTHERS THEN
RAISE warning 'unknown exception: % / %',SQLERRM, SQLSTATE;
END;
END LOOP;
END IF;
END IF;
RETURN NEW;
END;
$body$ LANGUAGE plpgsql volatile cost 100;
引用table:
CREATE TABLE reports.cobertura
(
lat DOUBLE PRECISION NOT NULL,
lng DOUBLE PRECISION NOT NULL,
id_provider INTEGER NOT NULL,
zoom INTEGER NOT NULL,
online BIGINT DEFAULT 0,
OFFLINE BIGINT DEFAULT 0,
CONSTRAINT cobertura_pkey PRIMARY KEY (lat, lng, id_provider),
CONSTRAINT cobertura_id_provider_fkey FOREIGN KEY (id_provider) REFERENCES reports.proveedor_movil (id_proveedor) match simple ON
UPDATE no action
ON
DELETE no action
)
WITH
(
oids=FALSE
);
-- Index: reports.cobertura_update
-- DROP INDEX reports.cobertura_update;CREATE INDEX cobertura_update
ON reports.cobertura
using btree
(
lat,
lng,
id_provider,
zoom
);
您可能会发现此查询对于识别死锁事务很有用:
with locks as (
select
pg_stat_activity.datname, application_name,
substr(pg_class.relname, 1, 32) relname, page, tuple,
pg_locks.transactionid as txid,
pg_locks.virtualtransaction as vtxid,
locktype, pg_locks.mode, pg_locks.granted, pg_stat_activity.usename,
substr(pg_stat_activity.query, 1, 255), pg_stat_activity.query_start,
age(now(), pg_stat_activity.query_start) as "age",
pg_stat_activity.pid -- , pg_locks.*
from pg_stat_activity, pg_locks
left outer join pg_class on (pg_locks.relation=pg_class.oid)
where
pg_locks.pid=pg_stat_activity.pid
and pg_stat_activity.pid != pg_backend_pid()
and (not granted OR pg_locks.mode like '%Exclusive%')
order by query_start
)
select * from locks a where not granted
or exists (select * from locks b where not b.granted
and COALESCE(a.relname, '')=COALESCE(b.relname, '')
and COALESCE(a.page, 0)=COALESCE(b.page, 0)
and COALESCE(a.tuple, 0)=COALESCE(b.tuple, 0)
and COALESCE(a.txid, '0'::xid)=COALESCE(b.txid, '0'::xid)
-- and COALESCE(a.vtxid, '')=COALESCE(b.vtxid, '')
);