10

ACCESS EXCLUSIVEPostgreSQL でテーブルを削除すると、参照されるテーブルのロックが必要になるのはなぜですか? ACCESS SHAREDこれをロックまたはロックなしに減らすにはどうすればよいですか? つまり、参照テーブルをロックせずにリレーションを削除する方法はありますか?

ドキュメントでどのロックが必要かについての言及を見つけることができませんが、同時操作中に複数のテーブルを削除するときに明示的に正しい順序でロックを取得しない限り、ログで AccessExclusiveLock を待機しているデッドロックを確認し、この制限を取得することができますよく参照されるテーブルのロックは、テーブルが削除されるときに他のプロセスに一時的な遅延を引き起こしています。

明確にするために、

CREATE TABLE base (
    id SERIAL,
    PRIMARY KEY (id)
);
CREATE TABLE main (
    id SERIAL,
    base_id INT,
    PRIMARY KEY (id),
    CONSTRAINT fk_main_base (base_id)
        REFERENCES base (id)
        ON DELETE CASCADE ON UPDATE CASCADE
);
DROP TABLE main; -- why does this need to lock base?
4

3 に答える 3

0

DDL は単純化のために、触れるものすべてを排他的にロックしていると思います — とにかく、通常の操作中に非一時テーブルを含む DDL を実行することは想定されていません。


デッドロックを回避するには、アドバイザリ ロックを使用できます。

start transaction;
select pg_advisory_xact_lock(0);
drop table main;
commit;

これにより、参照されるテーブルを含む DDL を同時に実行するクライアントが 1 つだけになるため、他のロックがどの順序で取得されるかは問題になりません。


最初に外部キーを削除することで、長時間テーブルをロックすることを避けることができます:

start transaction;
select pg_advisory_xact_lock(0);
alter table main drop constraint fk_main_base;
commit;
start transaction;
drop table main;
commit;

これはまだ排他的にロックする必要がありますbaseが、はるかに短い時間です。

于 2015-08-22T20:46:34.260 に答える
0
        -- SESSION#1
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

BEGIN;
CREATE TABLE base (
    id SERIAL
    , dummy INTEGER
    , PRIMARY KEY (id)
);
CREATE TABLE main (
    id SERIAL
    , base_id INTEGER
    , PRIMARY KEY (id)
    , CONSTRAINT fk_main_base FOREIGN KEY (base_id) REFERENCES base (id)
        -- comment the next line out ( plus maybe tghe previous one)
        ON DELETE CASCADE ON UPDATE CASCADE
);
        -- make some data ...
INSERT INTO base (dummy)
SELECT generate_series(1,10)
        ;

        -- make some FK references
INSERT INTO main(base_id)
SELECT id FROM base
WHERE random() < 0.5
        ;
COMMIT;

BEGIN;
DROP TABLE main; -- why does this need to lock base?

SELECT pg_backend_pid();

        -- allow other session to check the locks
        -- and attempt an update to "base"
SELECT pg_sleep(20);

        -- On rollback the other session will fail.
        -- On commit the other session will succeed.
        -- In both cases the other session must wait for us to complete.
-- ROLLBACK;
COMMIT;

        -- SESSION#2
        -- (Start this after session#1 from a different terminal)
SET search_path = tmp, pg_catalog;

PREPARE peeklock(text) AS
SELECT dat.datname
        , rel.relname as relrelname
        , cat.relname as catrelname
        , lck.locktype
        -- , lck.database, lck.relation
        , lck.page, lck.tuple
        -- , lck.virtualxid, lck.transactionid 
        -- , lck.classid
        , lck.objid, lck.objsubid
        -- , lck.virtualtransaction 
        , lck.pid, lck.mode, lck.granted, lck.fastpath

FROM pg_locks lck
LEFT JOIN pg_database dat ON dat.oid = lck.database
LEFT JOIN pg_class rel ON rel.oid = lck.relation
LEFT JOIN pg_class cat ON cat.oid = lck.classid
WHERE EXISTS(
        SELECT * FROM pg_locks l
        JOIN pg_class c ON c.oid = l.relation AND c.relname = $1
        WHERE l.pid =lck.pid
        )
        ;

EXECUTE peeklock( 'base' );
BEGIN;
        -- attempt to perfom some DDL
ALTER TABLE base ALTER COLUMN id TYPE BIGINT;

        -- attempt to perfom some DML
UPDATE base SET id = id+100;

COMMIT;

EXECUTE peeklock( 'base' );

\d base
SELECT * FROM base;
于 2015-08-22T19:40:21.340 に答える