PostgreSQL: возможные причины ошибки "обнаружена взаимоблокировка"

В настоящее время мы получаем взаимоблокировку следующего триггера, который вызывается одновременно несколькими потоками одновременно (не знаю, как определить точное число потоков).

Будем благодарны за любые подсказки, либо при обнаружении причины блокировки, либо даже лучше при решении проблемы.

Это работает на Enterprise DB 9.2.1.3 (PostgreSQL) на RHEL 6.4

Текущий код:

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;

Ссылочная таблица:

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
               );

1 ответ

Решение

Этот запрос может оказаться полезным для определения транзакций взаимоблокировки:

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, '')
);
Другие вопросы по тегам