UPDATE / INSERT 조합을 위해 Postgres에서 잠금


11

두 개의 테이블이 있습니다. 하나는 로그 테이블입니다. 다른 하나는 본질적으로 한 번만 사용할 수있는 쿠폰 코드를 포함합니다.

사용자는 쿠폰을 사용할 수 있어야합니다. 쿠폰을 사용하면 로그 테이블에 행을 삽입하고 쿠폰을 사용한 것으로 표시합니다 ( used열을 로 업데이트하여 true).

당연히 여기에는 명백한 경쟁 조건 / 보안 문제가 있습니다.

나는 mySQL의 세계에서 과거에 비슷한 일을 해왔다. 그 세계에서는 두 테이블을 전체적으로 잠그고 한 번에 한 번만 발생할 수 있다는 사실을 알고 안전한 논리를 수행 한 다음 완료되면 테이블을 잠금 해제합니다.

Postgres에 더 좋은 방법이 있습니까? 특히, 잠금이 전역 적이지만 걱정할 필요는 없습니다. 실제로 다른 사람이 해당 특정 코드를 입력하지 않도록해야하므로 일부 행 수준 잠금이 작동합니까?

답변:


15

나는 전에 MySQL에서와 같은 동시성 문제에 대해 들었다. Postgres에서는 그렇지 않습니다.

기본 READ COMMITTED트랜잭션 격리 수준 의 기본 제공 행 수준 잠금 이면 충분합니다.

한 테이블에서 다른 테이블로 직접 값을 전달하는 것이 편리하기 때문에 데이터 수정 CTE (MySQL에도없는 것)가있는 단일 명령문을 제안합니다. 당신은 아무것도 필요하지 않으면 coupon테이블에 별도의와 트랜잭션을 사용할 수 있습니다 UPDATEINSERT단지뿐만 아니라 문.

WITH upd AS (
   UPDATE coupon
   SET    used = true
   WHERE  coupon_id = 123
   AND    NOT used
   RETURNING coupon_id, other_column
   )
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;

하나 이상의 거래에서 동일한 쿠폰을 사용하려고 시도하는 경우 는 드 rare니다 . 그들은 고유 번호를 가지고 있지 않습니까? 같은 순간에 여러 번 시도하는 트랜잭션은 훨씬 드 물어야합니다. (아마도 응용 프로그램 버그이거나 누군가 게임을하려고합니까?)

그것이 무엇이든간에, UPDATE유일한 것은 하나의 거래에 대해서만 성공합니다 . 는 UPDATE취득 로우 레벨 잠금 업데이트하기 전에 각각의 타겟 행에있다. 동시 트랜잭션 UPDATE이 동일한 행을 시도하면 행에 잠금이 표시되고 블로킹 트랜잭션이 완료 될 때까지 기다린 후 ( ROLLBACK또는 COMMIT) 잠금 큐에서 첫 번째가됩니다.

  • 커밋 된 경우 조건을 다시 확인하십시오. 여전히 NOT used인 경우 행을 잠그고 진행하십시오. 그렇지 않으면이 UPDATE지금 어떤 규정 행을 발견하지 않으며하지 않습니다 아무것도 그렇게, 어떤 행을 반환하지 않습니다, INSERT또한 아무것도하지 않습니다.

  • 롤백 된 경우 행을 잠그고 진행하십시오.

경쟁 조건에 대한 가능성없습니다 .

동일한 트랜잭션에 더 많은 쓰기를 넣거나 다른 것보다 더 많은 행을 잠그지 않으면 교착 상태 가 발생할 가능성없습니다 .

INSERT관리-무료입니다. 실수로 coupon_id이미 log테이블에 있고 UNIQUE 또는 PK 제약 조건 log.coupon_id이있는 경우 고유 한 위반 후 전체 트랜잭션이 롤백됩니다. DB에 잘못된 상태를 나타냅니다. 위의 명령문이 log테이블 에 쓸 수있는 유일한 방법 인 경우 절대로 발생하지 않아야합니다.


실제로 하나 이상의 트랜잭션이 동일한 코드를 사용하려고 시도하는 것은 드문 일이지만 누군가가 시스템을 게임하려고 할 때 독점적으로 발생한다는 의심은 옳습니다. CTE는 Postgres로 이전하는 데 큰 도움이되었지만 암시 적 잠금이 이것으로 충분하다는 것을 알지 못했습니다.
Rob Miller
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.