스레드 안전 방식으로 값 (카운터)을 쿼리하고 늘리는 방법은 무엇입니까? (경쟁 조건을 피하십시오)


10

각 행에 카운터가있는 테이블 (정수 값)에서 현재 값을 가져 와서 동시에 증가시켜야합니다 .

효과적으로, 나는 이것을하고 싶다 :

SELECT counter FROM table WHERE id=123
UPDATE table SET counter=counter+1 WHERE id=123

그러나 두 개의 쿼리로 이것을 수행하는 것은 분명히 스레드 안전하지 않습니다. 동일한 행에서 동일한 작업을 수행하는 여러 프로세스가 동일한 카운터 값을 얻을 수 있습니다. 모두 고유해야하므로 각 프로세스는 실제 현재 값을 가져 와서 하나씩 증가시킵니다.

행당 수동 잠금을 구현하는 구성을 생각할 수 있지만 더 쉬운 방법이 있는지 궁금합니다.


아마도 거래를 사용합니까?
ypercubeᵀᴹ

답변:


15

업데이트 문은 이전에 선택하지 않아도 완벽하게 작동합니다! 단일 명령문은 정의상 안전하므로 동시에 두 개의 UPDATE 쿼리 만 수행하면 행이 두 번 증가합니다.

실제로 PHP 스크립트의 값을 선택하고 무언가를 수행하고 나중에이 정확한 카운터 값을 업데이트하려면 다음을 수행하십시오.

BEGIN;
SELECT `counter` FROM `table` WHERE `id` = 123 FOR UPDATE;
UPDATE `table` SET `counter` = `counter`+1 WHERE `id` = 123;
COMMIT;

그러면 새 트랜잭션이 시작되고 업데이트하려는 행을 선택하고 독점적으로 잠급니다. 그런 다음 다른 클라이언트가 내용을 변경하거나 잠긴 행에 액세스하지 않아도 걱정없이 안전하게 업데이트 할 수 있습니다. 마지막으로 변경 사항을 커밋해야합니다.

격리 수준 에 대한 내용도 읽어야 합니다 . READ UNCOMMITTED격리 수준 과 같은 값을 원하지 않을 수 있습니다. 이 사용 사례에는 다른 모든 것이 좋습니다.


나는 다른 곳에서 UPDATE table SET counter = counter + 1충분히 원자 적이 라는 것을 읽었 습니까? 여전히 거래 내역이 필요합니까?
CMCDragonkai

@CMCDragonkai 쿼리 자체는 원 자성이지만 이전 값을 선택하고 사용하지 않고 FOR UPDATE트랜잭션을 선택한 경우 선택한 값이 업데이트 쿼리에 사용 된 값과 다를 수 있습니다. 내 쿼리 조합은 값을 선택하자마자 행을 잠그므로이 정확한 카운터 값이 업데이트 쿼리에 사용됩니다.
GhostGambler

그래도 증분 이외의 작업을 수행하는 경우에만 필요합니까? 그것이 의미하는 것처럼, 혼자 원자 업데이트 쿼리가 충분하다면 충분합니다.
CMCDragonkai

1
@CMCDragonkai 열에 닿는 다른 쿼리를 실행하지 않으면 좋습니다.
GhostGambler
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.