애플리케이션에는 cargo_items라는 테이블이 있습니다. 나중에 이러한 항목을 처리하는 일종의 대기열로 볼 수 있습니다. 처음에는 3000 개의 항목을 가져 와서 하나씩 처리하는 단일 작업이있었습니다. 나중에 누군가 같은 작업의 다른 인스턴스 3 개를 시작하기로 결정했습니다. 무슨 일이 일어 났는지는 아주 분명합니다. 많은 항목이 두 번 처리되었습니다.
내 임무는 동시에 많은 인스턴스가 실행중인 경우 이러한 프로세스가 올바르게 작동하도록하는 것입니다. 내가 지금 가고있는 해결책은 데이터베이스에있는 3000 개의 항목을 job_id로 표시하고 나중에 이러한 모든 항목을 가져 와서 다른 프로세스와 분리하여 처리하는 것입니다.
이 행에 플래그를 지정하는 현재 접근 방식은 다음과 같습니다.
UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM (
SELECT id
FROM cargo_item
WHERE state='NEW' AND job_id is null
LIMIT 3000
FOR UPDATE
) sub
WHERE item.id = sub.id;
기본적으로이 접근 방식은 업데이트를 위해 3000 개의 행을 잠급니다. 그래도 좋은 접근 방식인지 확실하지 않습니다.
다른 스레드 에서이 시나리오에 대한 권고 잠금 사용에 대해 읽었습니다.
현재 접근 방식에 대해 어떻게 생각하고 대신 자문 잠금을 사용합니까?
제안 된대로 다음과 같이 업데이트 문을 수정합니다.
UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM (
SELECT id
FROM cargo_item
WHERE state='NEW' AND job_id is null
ORDER BY id
LIMIT 3000
FOR UPDATE
) sub
WHERE item.id = sub.id;
힌트는 Thx Erwin과 Tometzky입니다. 그럼에도 불구하고 문제를 해결하려는 방식이 좋은 것인지 궁금합니다. 당신이 생각할 다른 접근 방식이 있습니까?
관련 답변에서 다음을 참조하고 있습니다.
목표는 한 번에 한 행씩 잠그는 것 입니다. 동일한 트랜잭션에서 더 많은 행을 잠그려고하지 않는 한 교착 상태가 발생할 가능성 이 없기 때문에 권고 잠금이 있든 없든 잘 작동 합니다.
한 번에 3000 개의 행 을 잠 그려 는 경우 예제가 다릅니다 . 모든 동시 쓰기 작업이 동일한 일관된 순서로 행을 잠그는 경우를 제외하고 교착 상태 가 발생할 가능성 이 있습니다 . 문서 별 :
교착 상태에 대한 최선의 방어는 일반적으로 데이터베이스를 사용하는 모든 응용 프로그램이 일관된 순서로 여러 개체에 대한 잠금을 획득하도록하여이를 방지하는 것입니다.
하위 쿼리에서 ORDER BY를 사용하여 구현하십시오.
UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM (
SELECT id
FROM cargo_item
WHERE state='NEW' AND job_id is null
ORDER BY id
LIMIT 3000
FOR UPDATE
) sub
WHERE item.id = sub.id;
모든 트랜잭션이 동일한 순서로 잠금을 획득하고 순서 지정 열의 동시 업데이트가 예상되지 않는 한 이는 안전하고 신뢰할 수 있습니다. ( 매뉴얼의이 장 끝에있는 노란색 "주의"상자를 읽으십시오 .) 따라서 id
열 을 업데이트하지 않을 것이므로 귀하의 경우에 안전합니다 .
효과적으로 한 번에 하나의 클라이언트 만 이러한 방식으로 행을 조작 할 수 있습니다. 동시 트랜잭션은 동일한 (잠긴) 행을 잠그고 첫 번째 트랜잭션이 완료 될 때까지 기다립니다.
권고 잠금 은 동시 트랜잭션이 많거나 매우 오래 실행되는 경우에 유용합니다 (그렇지 않은 것 같음). 몇 개만 있으면 위의 쿼리 만 사용하고 동시 트랜잭션이 차례를 기다리도록하는 것이 전반적으로 더 저렴합니다.
동시 액세스는 설정 자체에서 문제가되지 않는 것 같습니다. 동시성은 현재 솔루션에서 발생하는 문제입니다.
대신 단일UPDATE
. n
각 UUID 에 번호 배치 (예 : 3000)를 할당하고 한 번에 모두 업데이트합니다. 가장 빨라야합니다.
UPDATE cargo_item c
SET job_id = u.uuid_col
, job_ts = now()
FROM (
SELECT row_number() OVER () AS rn, uuid_col
FROM uuid_tbl WHERE <some_criteria> -- or see below
) u
JOIN (
SELECT (row_number() OVER () / 3000) + 1 AS rn, item.id
FROM cargo_item
WHERE state = 'NEW' AND job_id IS NULL
FOR UPDATE -- just to be sure
) c2 USING (rn)
WHERE c2.item_id = c.item_id;
정수 나누기가 잘립니다. 처음 3000 개 행에 대해 1 개, 다음 3000 개 행에 대해 2 개를 얻습니다. 기타
나는 임의로 행을 선택 하고 특정 행을 할당 ORDER BY
하기 row_number()
위해 창에서 신청할 수 있습니다 .
디스패치 할 UUID 테이블 ( uuid_tbl
) 이없는 경우 VALUES
표현식을 사용 하여 제공합니다. 예.
3000 행의 일괄 처리를 얻습니다. 할당 할 3000의 배수를 찾지 못하면 마지막 배치는 3000이 부족합니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다