개발자가 착각했습니다. 당신은 필요 하나 SELECT ... FOR UPDATE
또는 행이 둘을 버전.
사용해보십시오. 열기 세 MySQL의 세션 (A)
, (B)
그리고 (C)
동일한 데이터베이스에.
에서 (C)
문제 :
CREATE TABLE test(
id integer PRIMARY KEY,
data varchar(255) not null,
version integer not null
);
INSERT INTO test(id,data,version) VALUES (1,'fred',0);
BEGIN;
LOCK TABLES test WRITE;
모두에서 (A)
와 (B)
발행 UPDATE
그 변화 테스트 및 설정 행 버전을, winner
당신은 어느 것이 어느 세션을 볼 수 있도록 각각의 텍스트를 :
-- In (A):
BEGIN;
UPDATE test SET data = 'winnerA',
version = version + 1
WHERE id = 1 AND version = 0;
-- in (B):
BEGIN;
UPDATE test SET data = 'winnerB',
version = version + 1
WHERE id = 1 AND version = 0;
이제에서 (C)
, UNLOCK TABLES;
잠금을 해제합니다.
(A)
및 (B)
행 잠금을 경주 할 것이다. 그들 중 하나가 이기고 자물쇠를 얻습니다. 다른 하나는 잠금 장치를 차단합니다. 자물쇠를 얻은 승자는 행 변경을 진행합니다. 가정 (A)
이 승자 라고 가정하면 이제로 변경된 행 (여전히 커밋되지 않은 다른 트랜잭션에는 표시되지 않음)을 볼 수 있습니다 SELECT * FROM test WHERE id = 1
.
이제 COMMIT
승자 세션에서이라고 말합니다 (A)
.
(B)
잠금이 설정되고 업데이트가 진행됩니다. 그러나 버전이 더 이상 일치하지 않으므로 행 수 결과에 의해보고 된대로 행이 변경되지 않습니다. 하나만 UPDATE
영향을 미쳤으며 클라이언트 응용 프로그램은 UPDATE
성공 및 실패를 명확하게 확인할 수 있습니다 . 추가 잠금이 필요하지 않습니다.
pastebin의 세션 로그를 참조하십시오 . 나는 mysql --prompt="A> "
세션 간의 차이점을 쉽게 알기 위해 etc를 사용했습니다. 시간 순서대로 인터리브 된 출력을 복사하여 붙여 넣었으므로 완전히 원시 출력이 아니므로 복사하여 붙여 넣는 데 오류가 발생했을 수 있습니다. 직접 테스트 해보십시오.
당신이했다면 하지 행 버전 필드를 추가, 다음, 당신은 할 필요가 SELECT ... FOR UPDATE
안정적으로 순서를 보장 할 수 있도록.
당신이 그것에 대해 생각한다면,의 데이터를 재사용하지 않고 즉시 수행 하거나 행 버전 관리를 사용 하는 경우 a SELECT ... FOR UPDATE
는 완전히 중복 됩니다 . 는 어쨌든 잠금을 취할 것입니다. 다른 사람이 읽기와 후속 쓰기 사이의 행을 업데이트하면 버전이 더 이상 일치하지 않으므로 업데이트가 실패합니다. 그것이 낙관적 잠금 작동 방식입니다.UPDATE
SELECT
UPDATE
목적 SELECT ... FOR UPDATE
은 다음과 같습니다.
- 교착 상태를 피하기 위해 잠금 순서를 관리합니다. 과
- 행에서 데이터를 읽으려고 할 때 행 잠금 범위를 확장하려면 응용 프로그램에서 데이터를 변경하고
SERIALIZABLE
격리 또는 행 버전 관리 를 사용하지 않고 원래 행을 기반으로하는 새 행을 작성하십시오 .
낙관적 잠금 (행 버전 관리) 및을 모두 사용할 필요는 없습니다 SELECT ... FOR UPDATE
. 하나 또는 다른 것을 사용하십시오.