SELECT-UPDATE 패턴 사용시 동시성 관리


25

다음 코드가 있다고 가정 해 봅시다 (끔찍하다는 것을 무시하십시오).

BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else

내 눈에 이것은 동시성을 올바르게 관리하지 않습니다. 거래가 있다고해서 다른 사람이 업데이트 진술을 받기 전에했던 것과 같은 가치를 읽지 않는다는 의미는 아닙니다.

이제 코드를 그대로 두십시오 (이것은 단일 명령문으로 처리되거나 자동 증분 / ID 열을 사용하는 것이 더 좋습니다) 동시성을 올바르게 처리하고 두 클라이언트가 동일한 결과를 얻을 수있는 경쟁 조건을 방지하는 확실한 방법 id 가치?

WITH (UPDLOCK, HOLDLOCK)SELECT에 a 를 추가 하면 트릭을 수행 할 것이라고 확신합니다 . SERIALIZABLE 트랜잭션 격리 수준은 (이것은 다른 사람이 트란이 끝날 때까지 당신이 무슨 짓을했는지 읽을 거부하기 때문에뿐만 아니라 작동하는 것 같다 것 UPDATE . :이 거짓 참조 마틴의 대답). 그게 사실입니까? 둘 다 똑같이 잘 작동합니까? 하나는 다른 것보다 선호됩니까?

ID 업데이트보다 합법적 인 일, 즉 업데이트해야하는 읽기를 기반으로 한 계산을 상상해보십시오. 많은 테이블이 관련 될 수 있는데, 그 중 일부는 쓰지 않을 것이고 다른 테이블은 쓰지 않을 것입니다. 가장 좋은 방법은 무엇입니까?

이 질문을 작성한 후에는 필요한 힌트 만 잠그기 때문에 잠금 힌트가 더 낫다고 생각하지만 다른 사람의 의견에 감사드립니다.

추신 : 그리고 가장 좋은 대답을 모르고 실제로 더 나은 이해를 원합니다! :)


명확히하기 위해 : 2 명의 클라이언트가 update더 이상 사용하지 않는 데이터를 기반으로 동일한 값을 읽거나 발행하지 못하게 하시겠습니까? 후자의 경우 rowversion열을 사용 하여 업데이트 할 행을 읽은 후 변경되지 않았는지 확인할 수 있습니다 .
a1ex07

첫 번째 클라이언트가 새 값으로 업데이트하기 전에 두 번째 클라이언트가 이전 id 값을 가져 오는 것을 원하지 않습니다. 차단해야합니다.
ErikE

답변:


11

SERIALIZABLE격리 수준 측면 만 해결하면 됩니다. 예, 이것은 작동하지만 교착 상태 위험이 있습니다.

두 트랜잭션 모두 행을 동시에 읽을 수 있습니다. 이들은 테이블 구조에 따라 오브젝트 S잠금 또는 인덱스 RangeS-S잠금을 취 하며이 잠금 은 호환되므로 서로를 차단하지 않습니다 . 그러나 업데이트에 필요한 잠금 (개체 IX잠금 또는 인덱스 RangeS-U) 을 획득하려고 할 때 서로를 차단 하여 교착 상태를 유발합니다.

UPDLOCK대신 명시 적 힌트를 사용하면 판독 값을 직렬화하여 교착 상태 위험을 피할 수 있습니다.


+1이지만 힙 테이블의 경우 업데이트 잠금을 사용해도 변환 교착 상태를 유지할 수 있습니다. sqlblog.com/blogs/alexander_kuznetsov/archive/2009/03/11/…
AK

기괴한, @alex. 실제로 UPDLOCK하기 전에 잠그는 것을 찾으려고하는 엔진의 경쟁 조건과 관련이 있다고 생각합니다.
ErikE

@ErikE - 알렉스의 문서 변환 교착 상태로 변환됩니다 IXX힙 자체에. 흥미롭게도 자격을 갖춘 행이 없으므로 행 잠금이 제거되지 않습니다. 왜 X잠금 이 필요한지 확실하지 않습니다 .
Martin Smith

11

가장 좋은 방법은 실제로 모듈을 높은 동시성에 노출시키고 직접 보는 것입니다. 때로는 UPDLOCK만으로도 충분하며 HOLDLOCK이 필요하지 않습니다. 때로는 sp_getapplock이 잘 작동합니다. 나는 여기에 담요 진술을하지 않을 것입니다-때로는 하나 이상의 인덱스, 트리거 또는 인덱싱 된 뷰를 추가하면 결과가 변경됩니다. 우리는 테스트 코드를 강조하고 사례별로 스스로를 확인해야합니다.

스트레스 테스트에 대한 몇 가지 예를 여기에 작성했습니다.

편집 : 내부 정보를 더 잘 이해하기 위해 Kalen Delaney의 책을 읽을 수 있습니다. 그러나 다른 문서와 마찬가지로 책이 동기화되지 않을 수 있습니다. 또한 6 가지 격리 수준, 여러 유형의 잠금, 클러스터 / 비 클러스터형 인덱스 및 기타 정보를 알고있는 조합이 너무 많습니다. 그것은 많은 조합입니다. 또한 SQL Server는 비공개 소스이므로 소스 코드를 다운로드하거나 디버깅 할 수 없으며 궁극적으로 지식의 원천이됩니다. 다음 릴리스 또는 서비스 팩 이후에 다른 사항이 불완전하거나 오래되었을 수 있습니다.

따라서 자체 스트레스 테스트없이 시스템에 적합한 것을 결정해서는 안됩니다. 읽은 내용이 무엇인지 이해하는 데 도움이 될 수 있지만 읽은 조언이 도움이된다는 것을 증명해야합니다. 아무도 당신을 위해 그것을 할 수 있다고 생각하지 않습니다.


9

이 특별한 경우에 UPDLOCK자물쇠를 추가 SELECT하면 이상을 방지 할 수 있습니다. HOLDLOCK업데이트 잠금이 트랜잭션 기간 동안 유지되므로 추가 가 필요하지 않지만 과거에 (나쁜) 습관으로 자신을 포함시키는 것을 고백합니다.

ID 업데이트보다 합법적 인 일을한다고 상상해보십시오. 일부 계산은 업데이트해야하는 읽기를 기반으로합니다. 많은 테이블이 관련 될 수 있는데, 그 중 일부는 쓰지 않을 것이고 다른 테이블은 쓰지 않을 것입니다. 가장 좋은 방법은 무엇입니까?

모범 사례는 없습니다. 동시성 제어 선택은 응용 프로그램 요구 사항을 기반으로해야합니다. 일부 응용 프로그램 / 트랜잭션은 데이터베이스에 대한 독점 소유권이있는 것처럼 실행하여 모든 비용으로 이상 및 부정확성을 피해야합니다. 다른 응용 프로그램 / 트랜잭션은 서로 어느 정도의 간섭을 허용 할 수 있습니다.

  • 웹 스토어에서 제품에 대한 밴딩 된 재고 레벨 (<5, 10+, 50+, 100+) 검색 = 더티 읽기 (정확하지 않은 것은 중요하지 않음).
  • 해당 웹 스토어에서 재고 수준 확인 및 감소 체크 아웃 = 반복 가능한 읽기 (판매하기 전에 재고가 있어야하며, 부정적인 재고 수준으로 끝나서는 안됩니다).
  • 은행에서 현재 계좌와 저축 계좌 간 현금 이동 = 직렬화 가능

편집 : @ AlexKuznetsov의 의견은 질문을 다시 읽고 내 대답에서 매우 명백한 오류를 제거하도록 촉구했습니다. 심야 게시에 대한 참고 사항.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.