낙관적 잠금과 비관적 잠금


571

낙관적 잠금과 비관적 잠금의 차이점을 이해합니다. 이제 내가 일반적으로 하나를 사용할 때 누군가가 나에게 설명 할 수 있습니까?

그리고이 질문에 대한 답변은 저장 프로 시저를 사용하여 쿼리를 수행하는지 여부에 따라 변경됩니까?

그러나 확인하기 만하면 낙관적 의미는 "읽는 동안 테이블을 잠그지 마십시오"를 의미하고 비관적 의미는 "읽는 동안 테이블을 잠그십시오"를 의미합니다.



1
그것은 직렬화 가능성에서 내가 읽었 기 때문에 특히 좋은 질문 At any technique type conflicts should be detected and considered, with similar overhead for both materialized and non-materialized conflicts입니다.
리틀 에일리언

1
여기 에서 낙관적 잠금근본 개념에 대한 좋은 설명을 볼 수 있습니다 .
Diego Mazzaro '12

답변:


812

낙관적 잠금 은 레코드를 읽고 버전 번호 (날짜, 타임 스탬프 또는 체크섬 / 해시를 포함하는 다른 방법)를 기록하고 레코드를 다시 쓰기 전에 버전이 변경되지 않았는지 확인하는 전략입니다. 레코드를 다시 쓰면 버전의 업데이트를 필터링하여 최신 상태인지 확인합니다. (즉, 버전을 확인하고 레코드를 디스크에 쓸 때 사이에 업데이트되지 않았습니다) 한 번에 버전을 업데이트하십시오.

레코드가 더러 우면 (즉, 다른 버전과 다름) 트랜잭션을 중단하고 사용자가 다시 시작할 수 있습니다.

이 전략은 세션의 데이터베이스에 대한 연결을 반드시 유지할 필요가없는 대용량 시스템 및 3 계층 아키텍처에 가장 적합합니다. 이 상황에서는 연결이 풀에서 이루어 지므로 클라이언트가 실제로 데이터베이스 잠금을 유지할 수 없으며 한 액세스에서 다음 액세스로 동일한 연결을 사용하지 않을 수 있습니다.

비관적 잠금 은 레코드가 끝날 때까지 독점 사용을 위해 레코드를 잠그는 것입니다. 낙관적 잠금보다 무결성이 훨씬 뛰어나지 만 교착 상태 를 피하려면 응용 프로그램 디자인에주의해야합니다 . 비관적 잠금을 사용하려면 일반적으로 2 계층 클라이언트 서버 응용 프로그램 에서와 같이 데이터베이스에 직접 연결 하거나 연결과 독립적으로 사용할 수있는 외부에서 사용 가능한 트랜잭션 ID가 필요합니다.

후자의 경우 TxID로 트랜잭션을 연 다음 해당 ID를 사용하여 다시 연결합니다. DBMS는 잠금을 유지하고 TxID를 통해 세션을 다시 선택할 수 있습니다. 이는 2 단계 커밋 프로토콜 (예 : XA 또는 COM + 트랜잭션 )을 사용하는 분산 트랜잭션이 작동하는 방식입니다.


148
낙관적 잠금은 반드시 버전 번호를 사용할 필요는 없습니다. 다른 전략에는 (a) 타임 스탬프 또는 (b) 행 자체의 전체 상태 사용이 포함됩니다. 후자의 전략은 추악하지만 스키마를 수정할 수없는 경우 전용 버전 열이 필요하지 않습니다.
Andrew Swan

2
@geek-XA와 같은 분산 트랜잭션 프로토콜을 사용하면 하나 이상의 시스템에서 별도의 트랜잭션 식별자를 그물 모양으로 만들 수 있습니다. 이 유형의 프로토콜은 트랜잭션 식별자가 세션에서 분리되고 명시 적으로 제공되므로 풀링 된 연결을 통해 잠금을 사용할 수 있습니다. 그러나 이로 인해 약간의 오버 헤드가 발생하고 응용 프로그램이 추적을 유지하는 데 신중하지 않으면 잠금 및 트랜잭션 식별자가 누출되기 쉽습니다. 훨씬 더 무거운 솔루션입니다.
ConcernedOfTunbridgeWells

22
@supercat-낙관적 잠금이 100 % 미만이라는 사실에 동의하지 않습니다. 지속 기간 동안 수정되지 않은 트랜잭션의 모든 입력 레코드를 확인하는 한 비관적 잠금 (업데이트 스타일 선택)만큼 정확합니다. 같은 기록. 가장 큰 차이점은 충돌이있는 경우에만 낙관적 잠금이 오버 헤드를 발생시키는 반면 비관적 잠금은 충돌시 오버 헤드가 감소한다는 것입니다. 따라서 대부분의 거래가 충돌하지 않는 경우 낙관적 인 것이 가장 좋습니다. 대개 대부분의 앱의 경우가 좋습니다.
RichVel

2
@Legends-낙관적 잠금을 사용하는 것은 웹 애플리케이션에 적합한 전략 일 것입니다.
ConcernedOfTunbridgeWells

2
선택은 읽기 대 쓰기 비율에 따라 달라집니다. 응용 프로그램이 주로 많은 사용자에 의해 읽기 전용 응용 프로그램이고 때로는 데이터를 쓰는 경우 낙관적 잠금을 사용하는 것보다 낫습니다. 예를 들어 StackOverflow는 많은 사람들이 페이지를 읽고 때로는 누군가가 페이지를 편집하도록합니다. 비관적 잠금에서는 누가 잠금을 얻습니까? 첫번째? 낙관적 잠금에서 페이지를 편집하려는 사람은 페이지의 마지막 버전이있는 한 페이지를 편집 할 수 있습니다.
jehon

177

많은 충돌이 예상되지 않는 경우 낙관적 잠금이 사용됩니다. 정상적인 작동에는 비용이 덜 들지만 충돌이 발생하면 거래가 중단 될 때 더 높은 가격을 지불하여 해결합니다.

비관적 잠금은 충돌이 예상 될 때 사용됩니다. 동기화를 위반하는 트랜잭션은 단순히 차단됩니다.

적절한 잠금 메커니즘을 선택하려면 읽기 및 쓰기 양을 추정하고 그에 따라 계획해야합니다.


일반적인 경우, 진술은 완벽하지만 대답에서 언급 한 @skaffman과 같이 부정확성을 허용 하는 CAS 작업을 관리 할 수있는 특별한 경우에는 실제로 달려 있다고 말할 것입니다.
Hearen

75

낙관론은 읽는 동안 아무것도 바뀌지 않을 것이라고 가정합니다.

비관론은 무언가가 그것을 잠그고 있다고 가정합니다.

데이터를 완벽하게 읽을 필요가없는 경우에는 낙관적입니다. 이상한 '더러운'을 읽을 수는 있지만 교착 상태 등이 발생할 가능성은 훨씬 적습니다.

대부분의 웹 응용 프로그램은 더티 읽기로 괜찮습니다. 드물기는하지만 데이터가 다음 재로드에서 정확하게 계산되지는 않습니다.

많은 금융 거래와 같이 정확한 데이터 작업을 위해서는 비관적입니다. 표시되지 않은 변경없이 데이터를 정확하게 읽는 것이 중요합니다. 추가 잠금 오버 헤드가 그만한 가치가 있습니다.

아, 그리고 Microsoft SQL 서버는 기본적으로 페이지 잠금으로 기본 설정되어 있습니다. 행 잠금이 더 정확하지만 훨씬 느립니다. 읽는 동안 교착 상태를 피하기 위해 트랜잭션을 읽기 커밋 또는 잠금 해제로 설정하는 것이 좋습니다.


JPA 낙관적 잠금을 사용하면 읽기 일관성을 보장 할 수 있습니다.
Gili

4
읽기 일관성은 별도의 관심사입니다. PostgreSQL, Oracle 및 기타 여러 데이터베이스를 사용하면 아직 커밋되지 않은 업데이트에 관계없이 일관된 데이터보기를 얻을 수 있으며 독점 ​​행 잠금의 영향을받지 않습니다.
RichVel

@RichVel에 동의해야합니다. 한편으로 트랜잭션 격리 수준이 READ UNCOMMITTED 인 경우 비관적 잠금으로 인해 더티 읽기를 방지하는 방법을 알 수 있습니다. 그러나 대부분의 데이터베이스 (겉으로는 MS SQL Server 포함)에 기본 읽기 수준 인 "READ COMMITTED"가 있다는 것을 언급하지 않고 낙관적 잠금이 더티 판독에 취약하다고 말하는 것은 오해의 소지가 있습니다. 비관적.
antinome

에릭 브로워 (Eric Brower)는 은행가들은 다른 은행들과 달리 더러운 운영을 선호한다고 말합니다. 당신의 전문가들은 트롤리에서 완전히 벗어난 것처럼 보입니다.
리틀 에일리언

1
에릭 브루어 (Eric Brewer)는 CAP 정리를 한 전문가로서 은행의 일관성에 대해 말한다 . 그것은 당신이 그것을 존중하는 것과 반대입니다.
Little Alien

50

이미 말한 것 외에도 :

  • 말했다해야 optimistic잠금 예측의 비용으로 동시성을 개선하는 경향이있다.
  • Pessimistic잠금은 동시성을 줄이는 경향이 있지만 더 예측 가능합니다. 당신은 돈 등을 지불 ...

3
비관적 잠금으로 예측 가능성 (어떻게 정의하든)이 어떻게 향상되는지 알 수 없습니다. '잠금이 일단 완료되면 트랜잭션을 완료 할 수 있습니다'라는 말이 맞다면 트랜잭션에 필요한 잠금이 모두 생길 때까지 지연이 발생할 수 있습니다 남은 잠금은 실제로 DB의 교착 상태 감지 + 확인 논리로 인해 중단 될 수 있습니다. 비관적 잠금을 사용하는 앱은 예상치 못한 실행 시간을 가질 수 있습니다. 고전적인 예는 누군가 레코드 X를 잠근 다음 점심 식사를 한 다음 사용자가 레코드 X와 Y를 잠근 다음 다른 Y와 Z를 잠그는 등 대부분의 사용자가 차단 될 때까지입니다. ..
RichVel

40

충돌을 다룰 때 두 가지 옵션이 있습니다.

  • 당신은 충돌을 피하려고 노력할 수 있으며, 그것이 비관적 잠금이하는 일입니다.
  • 또는 충돌 발생을 허용 할 수 있지만 트랜잭션 커밋시이를 감지해야합니다. 이것이 낙관적 잠금 기능입니다.

이제 다음과 같은 Lost Update 예외를 고려해 보겠습니다 .

업데이트 분실

손실 된 업데이트 이상은 읽기 커밋 된 격리 수준 에서 발생할 수 있습니다 .

위의 다이어그램에서 Alice는 자신이 40 명을 인출 할 수 있다고 생각 account하지만 Bob이 계정 잔액을 변경 한 사실을 알지 못했지만이 계정에는 20 개만 남았습니다.

비관적 잠금

비관적 잠금은 계정에서 공유 또는 읽기 잠금을 수행하여 Bob이 계정을 변경하지 못하도록하여이 목표를 달성합니다.

잃어버린 업데이트 비관적 잠금

위의 다이어그램에서 Alice와 Bob은 account두 사용자가 모두 읽은 테이블 행 에 대한 읽기 잠금을 획득합니다 . 데이터베이스는 반복 가능한 읽기 또는 직렬화 가능을 사용할 때 SQL Server에서 이러한 잠금을 획득합니다.

Alice와 Bob은 모두 accountPK 값 이 인을 읽었으므로 1한 사용자가 읽기 잠금을 해제 할 때까지 둘 다 변경할 수 없습니다. 쓰기 작업에는 쓰기 / 독점 잠금 획득이 필요하고 공유 / 읽기 잠금은 쓰기 / 독점 잠금을 방지하기 때문입니다.

Alice가 트랜잭션을 커밋하고 읽기 잠금이 account행에서 해제 된 후에 만 Bob UPDATE이 재개하고 변경 사항을 적용합니다. Alice가 읽기 잠금을 해제 할 때까지 Bob의 UPDATE는 차단됩니다.

데이터 액세스 프레임 워크가 기본 데이터베이스 비관적 잠금 지원을 사용하는 방법에 대한 자세한 내용은 이 기사를 확인 하십시오 .

낙관적 잠금

낙관적 잠금은 충돌이 발생하지만 버전이 변경됨에 따라 Alice의 UPDATE를 적용 할 때 충돌을 감지합니다.

응용 프로그램 수준의 트랜잭션

이번에는 추가 version열이 있습니다. version열 때마다 업데이 트를 증가 또는 DELETE가 실행되고, 그것은 또한 UPDATE 및 DELETE 문장의 WHERE 절에 사용됩니다. 이 작업 version을 수행하려면 UPDATE 또는 DELETE를 실행하기 전에 SELECT를 발행하고 현재를 읽어야합니다. 그렇지 않으면 WHERE 절에 전달하거나 증가시킬 버전 값을 알 수 없습니다.

데이터 액세스 프레임 워크가 낙관적 잠금을 구현하는 방법에 대한 자세한 내용은 이 기사를 확인 하십시오 .

응용 프로그램 수준의 트랜잭션

관계형 데이터베이스 시스템은 클라이언트가 일반적으로 터미널을 통해 메인 프레임에 연결할 때 70 년대 후반 80 년대 초에 등장했습니다. 이것이 데이터베이스 시스템이 세션 설정과 같은 용어를 정의하는 이유입니다.

오늘날 인터넷을 통해 더 이상 동일한 데이터베이스 트랜잭션의 컨텍스트에서 읽기 및 쓰기를 실행하지 않으며 ACID로는 더 이상 충분하지 않습니다.

예를 들어 다음 사용 사례를 고려하십시오.

여기에 이미지 설명을 입력하십시오

낙관적 잠금이 없으면 데이터베이스 트랜잭션이 Serializable을 사용하더라도이 손실 된 업데이트가 발견되지 않았을 것입니다. 읽기 및 쓰기가 별도의 HTTP 요청으로 실행되므로 다른 데이터베이스 트랜잭션에서 실행되기 때문입니다.

따라서 낙관적 잠금 기능을 사용하면 사용자가 생각하는 시간도 포함 된 응용 프로그램 수준 트랜잭션을 사용할 때도 업데이트 손실을 방지 할 수 있습니다.

응용 프로그램 수준 또는 논리적 트랜잭션에 대한 자세한 내용은 이 문서를 확인 하십시오 .

결론

낙관적 잠금은 매우 유용한 기술이며, 커밋 된 읽기와 같이 덜 엄격한 격리 수준을 사용하거나 후속 데이터베이스 트랜잭션에서 읽기 및 쓰기가 실행될 때에도 제대로 작동합니다.

낙관적 잠금의 단점은를 포착 할 때 데이터 액세스 프레임 워크에 의해 롤백이 트리거되어 OptimisticLockException현재 실행중인 트랜잭션에 의해 이전에 수행 한 모든 작업이 손실된다는 것입니다.

경합이 많을수록 충돌이 많으며 거래 중단 가능성이 높아집니다. 테이블 행과 인덱스 레코드가 모두 포함될 수있는 현재 보류중인 모든 변경 사항을 되돌려 야하므로 데이터베이스 시스템에 대한 롤백 비용이 많이들 수 있습니다.

이러한 이유로 트랜잭션이 롤백 될 가능성을 줄이기 때문에 충돌이 자주 발생하는 경우 비관적 잠금이 적합 할 수 있습니다.


어떤 시나리오에서 OptimisticLocking 및 PessimisticLocking을 선택하도록 제안 하시겠습니까? OptimisticLockException이 얼마나 자주 발생하는지에 따라 달라 집니까?
Stimpson Cat

1
유스 케이스에 따라 다릅니다. 때로는 낙관적 잠금이 유일한 솔루션 (예 : 다중 요청 트랜잭션)입니다. 다른 경우에는 비관적 잠금이 유일한 솔루션입니다 (예 : PostgreSQL 권고 잠금 ). 경우에 따라와 같이 이들을 결합해야하는 경우가 PESSIMISTIC_FORCE_INCREMENT있습니다.
Vlad Mihalcea

22

비관적 잠금이 더 나은 선택이 될 경우를 한 번 더 생각할 것입니다.

낙관적 잠금을 위해 데이터 수정의 모든 참가자는 이러한 종류의 잠금 사용에 동의해야합니다. 그러나 누군가가 버전 열을 신경 쓰지 않고 데이터를 수정하면 낙관적 잠금의 전체 아이디어를 망칠 것입니다.


낙관적이고 비관적 인 잠금을 사용하려는 사람들도 서로의 발을 밟을 수 있습니다. 비관적 세션이 레코드를 업데이트하는 동안 낙관적 세션이 레코드를 읽고 일부 계산을 수행하는 시나리오를 상상 한 다음, 낙관적 세션이 되돌아 와서 동일한 변경 사항을 기록하지 않고 동일한 레코드를 업데이트한다고 가정하십시오. 모든 세션에서 동일한 구문을 사용하는 경우에만 업데이트를 위해 ...를 선택하십시오.
22:09에

좋은 설명, 당신은 나에게서 투표를 줘
Dulaj Kulathunga

15

기본적으로 두 가지 가장 인기있는 답변이 있습니다. 첫 번째 는 기본적으로 말합니다

Optimistic에는 세션을 위해 데이터베이스에 대한 연결을 반드시 유지할 필요가없는 3 계층 아키텍처가 필요하지만 Pessimistic Locking은 레코드가 끝날 때까지 독점 사용을 위해 레코드를 잠글 때입니다. 데이터베이스에 직접 연결해야하는 낙관적 잠금보다 무결성이 훨씬 뛰어납니다.

또 다른 대답은

잠금이 없기 때문에 낙관적 (버전 관리)이 빠르지 만 경합이 높을 때 (비관적) 잠금이 더 잘 수행되며 작업을 버리고 다시 시작하지 않고 작업을 방지하는 것이 좋습니다.

또는

낙관적 인 충돌이 발생할 때 낙관적 잠금이 가장 효과적입니다.

이 페이지에 수록 되어 있습니다.

"연결 유지"가 "낮은 충돌"과 어떻게 관련되어 있는지 설명하기 위해 대답을 만들었습니다.

어떤 전략이 가장 적합한 지 이해하려면 DB의 초당 트랜잭션이 아니라 단일 트랜잭션 기간에 대해 생각하십시오. 일반적으로 trasnaction을 열고 작업을 수행하고 트랜잭션을 닫습니다. 이것은 ANSI가 짧고 고전적인 트랜잭션으로 잠금을 피하기에 좋습니다. 그러나 많은 고객이 같은 방 / 좌석을 동시에 예약하는 티켓 예약 시스템을 어떻게 구현합니까?

오퍼를 탐색하고 사용 가능한 많은 옵션과 현재 가격으로 양식을 채우십시오. 시간이 많이 걸리고 옵션이 더 이상 사용되지 않을 수 있습니다. 액세스 한 데이터에 대한 잠금이 없었고 다른 사람이 더 민첩하고 다른 방식으로 액세스했기 때문에 양식을 작성하기 시작하고 "동의 함"버튼을 누르기 전에 유효하지 않은 모든 가격 모든 가격을 변경하고 새 가격으로 다시 시작해야합니다.

대신 모든 옵션을 잠글 수 있습니다. 이것은 비관적 인 시나리오입니다. 왜 짜증나는지 알 수 있습니다. 예약을 시작하고 담배를 피우는 한 명의 광대가 당신의 시스템을 무너 뜨릴 수 있습니다. 그가 끝내기 전에는 아무것도 예약 할 수 없습니다. 현금 흐름이 0으로 떨어집니다. 그렇기 때문에 현실적으로 낙관적 예약이 사용됩니다. 너무 오래 머무는 사람들은 더 높은 가격으로 예약을 다시 시작해야합니다.

이 낙관적 인 접근 방식에서는 읽은 모든 데이터를 기록해야하며 ( 반복 된 읽기 와 같이) 데이터 버전으로 커밋 포인트에 도달해야합니다 (현재 가격이 아닌이 견적에 표시된 가격으로 주식을 사고 싶습니다) ). 이 시점에서 ANSI 트랜잭션이 생성되어 DB를 잠그고 변경 사항이 없는지 확인하고 작업을 커밋 / 중단합니다. IMO는 Optimistic CC 와도 관련이있는 MVCC의 효과적인 에뮬레이션 이며 중단시 트랜잭션이 다시 시작된다고 가정합니다. 즉, 새 예약을합니다. 여기서의 거래에는 사용자의 결정이 포함됩니다.

MVCC를 수동으로 구현하는 방법을 이해하지 못했지만 재시작 옵션이있는 장기 실행 트랜잭션이 주제를 이해하는 열쇠라고 생각합니다. 내가 어딘가 잘못하면 나를 바로 잡으십시오. 나의 대답은 이 Alex Kuznecov 장 에서 동기를 부여 받았다 .


12

대부분의 경우 낙관적 잠금이 더 효율적이며 더 높은 성능을 제공합니다. 비관적 잠금과 낙관적 잠금 중에서 선택할 때 다음을 고려하십시오.

  • 비관적 잠금은 업데이트가 많고 사용자가 데이터를 동시에 업데이트하려고 할 가능성이 높은 경우에 유용합니다. 예를 들어, 각 작업에서 한 번에 많은 수의 레코드를 업데이트 할 수 있고 (은행에서 매월 말에 모든 계정에이자 수입을 추가 할 수 있음) 두 응용 프로그램이 동시에 이러한 작업을 실행하는 경우 충돌이 발생합니다 .

  • 비관적 잠금은 자주 업데이트되는 작은 테이블이 포함 된 응용 프로그램에서도 더 적합합니다. 이러한 소위 핫스팟의 경우 충돌이 발생할 가능성이 매우 높아 낙관적 잠금으로 인해 충돌하는 트랜잭션을 롤백 할 때 낭비가됩니다.

  • 낙관 가능성은 충돌 가능성이 매우 낮은 경우에 유용합니다. 레코드는 많지만 사용자는 적거나 업데이트 및 읽기 유형 작업은 거의 없습니다.


3

낙관적 잠금의 사용 사례 중 하나는 응용 프로그램이 데이터베이스를 사용하여 스레드 / 호스트 중 하나가 작업을 '확보'하도록하는 것입니다. 이것은 정기적으로 저에게 유용한 기술입니다.

내가 생각할 수있는 가장 좋은 예는 여러 스레드가 작업을 동시에 요구하는 데이터베이스를 사용하여 구현 된 작업 대기열입니다. 작업의 상태가 'Available', 'Claimed', 'Completed'인 경우 db 쿼리는 "Set status = 'Claimed'where status = 'Available'과 같이 말할 수 있습니다. 여러 스레드가 이러한 방식으로 상태를 변경하려고하면, 더티 데이터로 인해 첫 번째 스레드를 제외한 모든 스레드가 실패합니다.

이것은 낙관적 잠금 만 사용하는 유스 케이스입니다. 따라서 "많은 충돌이 예상되지 않을 때 최적화 잠금이 사용된다"는 대안으로 충돌이 예상되지만 정확히 하나의 트랜잭션이 성공하기를 원하는 경우에도 사용할 수 있습니다.


3

낙관적이고 비관적 인 잠금에 대해 많은 좋은 말이 있습니다. 고려해야 할 중요한 사항은 다음과 같습니다.

낙관적 잠금을 사용하는 경우 애플리케이션이 이러한 장애로부터 어떻게 복구 될 것인지에 대해 신중해야합니다.

특히 비동기식 메시지 구동 아키텍처에서는 이로 인해 잘못된 메시지 처리 또는 업데이트 손실이 발생할 수 있습니다.

장애 시나리오를 고려해야합니다.

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