3

그래서 Postgresql의 외래 키 제약 조건 처리에 대해 혼란스러워하고 있습니다. (버전 8.4.4).Postgresql : 외래 키 제약 조건 평가에서 암시 적 잠금 획득

우리는 약간 아래에 익명 테이블의 몇 가지를 가지고 :

device: 
    (id, blah, blah, blah, blah, blah x 50)… 
    primary key on id 
    whooooole bunch of other junk 

device_foo: 
    (id, device_id, left, right) 
    Foreign key (device_id) references device(id) on delete cascade; 
    primary key on id 
    btree index on 'left' and 'right' 

은 그래서 일부 쿼리를 실행하는 두 개의 데이터베이스 창에 착수했다.

db1> begin; lock table device in exclusive mode; 
db2> begin; update device_foo set left = left + 1; 

db2 연결 블록.

device_stuff에서 '왼쪽'열의 업데이트가 장치 테이블의 활동에 영향을 받는다는 것은 이상한 것 같습니다. 하지만 그것은. 사실, 나는 DB1로 돌아갈 경우 :

db1> select * from device_stuff for update; 
      *** deadlock occurs *** 

pgSQL의 로그는 다음과 같습니다

blah blah blah deadlock blah. 
CONTEXT: SQL statement "SELECT 1 FROM ONLY "public"."device" x WHERE "id" OPERATOR(pg_catalog.=) $1 FOR SHARE OF X: update device_foo set left = left + 1; 

나는 두 가지 문제를 가지고 가정 : 첫 번째는 내가 이해하지 않는다는 것입니다 이러한 종류의 잠금이 발생하는 정확한 메커니즘. 문을 호출하는 어떤 종류의 잠금을 보려면 pg_locks를 쿼리하는 몇 가지 유용한 쿼리가 있지만 난 update device_foo 명령을 단독으로 실행하면이 특정 종류의 잠금을 관찰 할 수 없었습니다. (아마도 나는 잘못된 것을하고 있습니다.) 또한 외래 키 제약 조건 검사의 잠금 획득 동작에 대한 문서를 찾을 수 없습니다. 내가 가진 것은 로그 메시지뿐입니다. 이 행을 변경하면 외래 키가있는 모든 테이블에서 업데이트 잠금이 획득됩니다.

두 번째 문제는 그런 일이 발생하지 않도록하는 방법을 찾고 싶습니다. 실제 응용 프로그램에서 가끔 교착 상태가 발생합니다. 나는 device_foo에있는 모든 행에 영향을주는 큰 업데이트 문을 장치 테이블에서 큰 잠금을 획득하지 않고 실행할 수 있기를 바랍니다. (이 접근이 device 테이블에서 벌어지고 많은, 그리고 그것을 얻기 위해 비싼 잠금의 종류입니다.)

가 전용 모드에서

답변

0

잠금 테이블이 없음 프로세스가 그 테이블을 읽을 수 있다는 것을 의미하고, 외래 키를 확인 테이블 장치를 읽어야합니다.

+0

하지만 외래 키 제약 조건을 확인해야하는 이유는 무엇입니까? 외래 키 열을 변경하지 않습니다. – fennec

2

lock table device in exclusive mode은 테이블 ("exclusive mode")에 대해 매우 제한적인 잠금을 취합니다. 상위 테이블에 외래 키가있는 테이블을 수정하면 상위 테이블에 대한 무해한 공유 잠금이 적용됩니다 (테이블을 참조하는 행이 잠재적으로 업데이트되는 동안 테이블을자를 수 없음).

사실 지금 시도해 보면 잠금 동작을 재현 할 수 없습니다 (8.4.4). 내가 한 두 개의 동시 연결에 다음

create table device(device_id serial primary key, value text not null); 
create table device_foo(device_foo_id serial primary key, device_id int not null references device(device_id) on delete cascade, value text not null); 
insert into device(value) values('FOO'),('BAR'),('QUUX'); 
insert into device_foo(device_id, value) select device_id, v.value from (values('mumble'),('grumble'),('fumble')) v(value), device; 

과 : 내가 그랬어

<1>=# begin; lock table device in exclusive mode; 
<2>=# begin; update device_foo set value = value || 'x'; 

이것은 당신이 무슨 일을하는지에 해당 될 나에게 나타납니다,하지만 두 번째 세션 잠금을하지 않는다 - 예상대로 "UPDATE 9"를 즉시 제공합니다. device_foo 블록에 삽입하면 예상대로 업데이트 문이 device_id 열로 설정됩니다. db2 세션에서 db1 세션의 pg_locks에있는 ExclusiveLock을 볼 수 있습니다.또한 "select * from device for share"를 수행하면 차단됩니다. 이는 교착 상태 오류에서 표시되는 문장입니다. 또한 db2가 device_foo의 device_id 열을 업데이트하려고 시도하는 동안 db1 연결에서 "select * from update_foo from update"를 수행하면 교착 상태가 발생하지 않습니다.

행을 업데이트하면 행이 잠김으로 표시되지만 해당 잠금은 pg_locks에 표시되지 않습니다. 행을 갱신하는 중에 테이블을 h 제/절단/재 색인화하려는 g 용자를 잠그기 위해 테이블을 잠근다.

동시 업데이트에 대해 device 테이블을 잠 그려면 덜 엄격한 잠금 모드가 필요할 수 있습니다. manual은 이러한 종류의 활동에 대해 "행을 독점 공유"할 것을 제안합니다. 이것은 "독점"에서 한 단계 아래에 있지만 "select ... for share"문과 호환됩니다.

그래서 실제로 열려있는 질문은 "공유 ... 선택"쿼리를 실행하는 것입니다. : -S 그것은 외래 키 무결성을 주장하기위한 문장처럼 보이지만 재현 할 수는 없습니다.

+0

이 동작이 Postgres의 표준이 아닌 것 같습니다. (나는 그 인상을 받고 있었다). 그래서 제가 테이블을 뒤 틀어서 무엇이 눈에 띄는 지 보겠습니다. 그리고, 내가 사용하고 있던 '독점 모드'는 문제를 분리하는 것이 었습니다. 실제로는 정기적으로 즐기는 것이 아닙니다. 실제 문제는 'update' 문이'device '주변에 흩어져있는 다른 잠금과 충돌 할 때 발생합니다. – fennec

관련 문제