동시 쓰기 액세스없이
CTE 에서 선택 사항을 구체화하고 의 FROM
조항에 참여하십시오 UPDATE
.
WITH cte AS (
SELECT server_ip -- pk column or any (set of) unique column(s)
FROM server_info
WHERE status = 'standby'
LIMIT 1 -- arbitrary pick (cheapest)
)
UPDATE server_info s
SET status = 'active'
FROM cte
WHERE s.server_ip = cte.server_ip
RETURNING server_ip;
원래 여기에는 일반 하위 쿼리가 있었지만 Feike가 지적한 LIMIT
것처럼 특정 쿼리 계획에 대해 회피 할 수 있습니다 .
플래너는 이상 중첩 루프를 실행하는 계획을 생성하도록 선택할 수 있습니다 LIMITing
더 많은 원인이 서브 쿼리 UPDATEs
보다 LIMIT
예를 :
Update on buganalysis [...] rows=5
-> Nested Loop
-> Seq Scan on buganalysis
-> Subquery Scan on sub [...] loops=11
-> Limit [...] rows=2
-> LockRows
-> Sort
-> Seq Scan on buganalysis
테스트 케이스 재생
위의 문제를 해결하는 방법은 LIMIT
하위 쿼리를 자체 CTE 로 래핑하는 것입니다. CTE가 구체화되었으므로 중첩 루프의 다른 반복에서 다른 결과를 반환하지 않습니다.
또는 간단한 경우에 상관 관계 가 낮은 하위 쿼리 를 사용하십시오LIMIT
1
. 더 간단하고 빠르게 :
UPDATE server_info
SET status = 'active'
WHERE server_ip = (
SELECT server_ip
FROM server_info
WHERE status = 'standby'
LIMIT 1
)
RETURNING server_ip;
동시 쓰기 액세스
이 모든 것에 대한 기본 격리 수준READ COMMITTED
을 가정 합니다. 엄격한 격리 수준 ( REPEATABLE READ
및 SERIALIZABLE
)으로 인해 직렬화 오류가 발생할 수 있습니다. 보다:
동시 쓰기로드에서 FOR UPDATE SKIP LOCKED
경쟁 조건을 피하기 위해 행을 잠 그려면 추가하십시오 . SKIP LOCKED
Postgres 9.5 에 추가되었으며 , 이전 버전은 아래를 참조하십시오. 매뉴얼 :
를 사용하면 SKIP LOCKED
즉시 잠글 수없는 선택된 행을 건너 뜁니다. 잠긴 행을 건너 뛰면 데이터가 일관되지 않으므로 일반적인 작업에는 적합하지 않지만 대기열과 같은 테이블에 액세스하는 여러 소비자와의 잠금 경합을 피하는 데 사용할 수 있습니다.
UPDATE server_info
SET status = 'active'
WHERE server_ip = (
SELECT server_ip
FROM server_info
WHERE status = 'standby'
LIMIT 1
FOR UPDATE SKIP LOCKED
)
RETURNING server_ip;
규정 된 잠금 해제 된 행이 남아 있지 않으면이 쿼리에서 아무 행도 발생하지 않으며 (행이 업데이트되지 않음) 빈 결과가 나타납니다. 중요하지 않은 작업의 경우 완료된 것입니다.
그러나 동시 트랜잭션에 행이 잠겨있을 수 있지만 업데이트 ( ROLLBACK
또는 다른 이유로)를 완료하지 마십시오 . 확인하려면 실행 최종 검사 :
SELECT NOT EXISTS (
SELECT 1
FROM server_info
WHERE status = 'standby'
);
SELECT
잠긴 행도 볼 수 있습니다. 을 반환하지 않는 true
하나 이상의 행이 계속 처리되고 있으며 트랜잭션을 계속 롤백 할 수 있습니다. (또는 새로운 행 사이에 추가되었습니다.) 조금, 다음 루프 두 단계를 기다립니다 ( UPDATE
다시 더 행을 얻을 때까지, SELECT
...) 당신이 얻을 때까지 true
.
관련 :
없이 SKIP LOCKED
PostgreSQL을에 9.4 이상
UPDATE server_info
SET status = 'active'
WHERE server_ip = (
SELECT server_ip
FROM server_info
WHERE status = 'standby'
LIMIT 1
FOR UPDATE
)
RETURNING server_ip;
동일한 행을 잠 그려는 동시 트랜잭션은 첫 번째 행이 잠금을 해제 할 때까지 차단됩니다.
첫 번째 롤백 인 경우 다음 트랜잭션이 잠금을 수행하고 정상적으로 진행됩니다. 대기열에있는 다른 사람들은 계속 기다립니다.
처음 커밋 된 경우 WHERE
조건이 다시 평가되고 TRUE
더 이상 status
변경 되지 않은 경우 CTE는 (어쩌면 놀랍게도) 행을 반환하지 않습니다. 아무 반응이 없습니다. 모든 트랜잭션을 업데이트 할 때 즉, 원하는 동작의 같은 행을 .
그러나 각 트랜잭션 업데이트 싶어하지 않을 경우 다음 행을 . 그리고 우리는 임의의 (또는 임의의 ) 행 을 업데이트하고 싶기 때문에 전혀 기다릴 필요가 없습니다.
권고 잠금을 사용 하여 상황을 차단 해제 할 수 있습니다 .
UPDATE server_info
SET status = 'active'
WHERE server_ip = (
SELECT server_ip
FROM server_info
WHERE status = 'standby'
AND pg_try_advisory_xact_lock(id)
LIMIT 1
FOR UPDATE
)
RETURNING server_ip;
이런 식으로, 아직 잠기지 않은 다음 행 이 업데이트됩니다. 각 트랜잭션마다 새로운 행이 생깁니다. 이 기술을 위해 Czech Postgres Wiki의 도움을 받았습니다 .
id
고유 인 bigint
열 (또는 같은 암시 적 캐스트와 모든 유형 int4
또는 int2
).
자문 잠금 장치가 동시에 데이터베이스에 여러 테이블에 대한 사용중인 경우로 명확하게 pg_try_advisory_xact_lock(tableoid::int, id)
- id
고유 인 integer
여기.
이후 tableoid
A는 bigint
양이, 그것은 이론적으로 오버 플로우 수 있습니다 integer
. 편집증이 충분하다면 (tableoid::bigint % 2147483648)::int
대신에 사용하십시오-진정한 편집증에 이론적 인 "해시 충돌"을 남겨 두십시오 ...
또한 Postgres는 WHERE
어떤 순서로든 조건을 자유롭게 테스트 할 수 있습니다. 이 수 를 테스트 pg_try_advisory_xact_lock()
하고 잠금을 획득 하기 전에 status = 'standby'
관련이없는 행에 대한 자세한 자문 잠금을 초래할 수있는, status = 'standby'
사실이 아니다. SO 관련 질문 :
일반적으로 이것을 무시할 수 있습니다. 규정 된 행만 잠기도록 하기 위해 위와 같은 CTE에 술어를 중첩 시키거나 OFFSET 0
해킹이 있는 서브 쿼리를 중첩시킬 수 있습니다 (인라인 방지) . 예:
또는 (순차 스캔을 위해 저렴) CASE
다음과 같은 명령문에 조건을 중첩시킵니다 .
WHERE CASE WHEN status = 'standby' THEN pg_try_advisory_xact_lock(id) END
그러나CASE
트릭도에 인덱스를 사용하는 포스트 그레스를 유지하는 것입니다 status
. 이러한 색인이 사용 가능한 경우, 추가 중첩이 필요하지 않습니다. 규정 된 행만 색인 스캔에서 잠 깁니다.
모든 호출에 인덱스가 사용되는지 확신 할 수 없으므로 다음과 같이 할 수 있습니다.
WHERE status = 'standby'
AND CASE WHEN status = 'standby' THEN pg_try_advisory_xact_lock(id) END
은 CASE
논리적으로 중복이지만, 서버 논의 된 목적.
명령이 긴 트랜잭션의 일부인 경우 수동으로 해제 할 수 있고 해제해야하는 세션 수준 잠금을 고려하십시오. 따라서 잠긴 행을 마치면 바로 잠금을 해제 할 수 있습니다 : pg_try_advisory_lock()
및pg_advisory_unlock()
. 매뉴얼 :
세션 레벨에서 획득 한 후에는 명시 적으로 해제되거나 세션이 종료 될 때까지 권고 잠금이 유지됩니다.
관련 :