웹 애플리케이션에서 경쟁 조건을 방지하는 방법은 무엇입니까?


31

Alice와 Bob이 모두 제품 목록을 편집하는 전자 상거래 사이트를 생각해보십시오. Alice는 설명을 개선하고 Bob은 가격을 업데이트합니다. Acme Wonder Widget을 동시에 편집하기 시작합니다. 밥은 먼저 마무리하고 새로운 가격으로 제품을 저장합니다. Alice는 설명을 업데이트하는 데 시간이 조금 더 걸리며 완료되면 새 설명과 함께 제품을 저장합니다. 불행히도, 그녀는 의도하지 않은 이전 가격으로 가격을 덮어 씁니다.

내 경험상 이러한 문제는 웹 앱에서 매우 일반적입니다. 일부 소프트웨어 (예 : wiki 소프트웨어)는이를 방지합니다. 일반적으로 "편집 중 페이지가 업데이트되었습니다"라는 두 번째 저장에 실패합니다. 그러나 대부분의 웹 사이트에는이 보호 기능이 없습니다.

컨트롤러 메소드 자체가 스레드로부터 안전하다는 점은 주목할 가치가 있습니다. 일반적으로 데이터베이스 트랜잭션을 사용하므로 Alice와 Bob이 정확한 순간에 저장하려고하면 손상이 발생하지 않는다는 점에서 안전합니다. 경쟁 조건은 브라우저에 오래된 데이터가있는 Alice 또는 Bob에서 발생합니다.

이러한 경쟁 조건을 어떻게 방지 할 수 있습니까? 특히 알고 싶습니다.

  • 어떤 기술을 사용할 수 있습니까? 예 : 마지막 변경 시간 추적. 각각의 장단점은 무엇입니까?
  • 유용한 사용자 경험은 무엇입니까?
  • 이 보호 기능은 어떤 프레임 워크에 내장되어 있습니까?

이미 개체의 변경 날짜를 추적하고 다른 변경 사항이 업데이트하려고하는 데이터의 수명과 비교하여 답을 얻었습니다. 효율적으로하는 방법과 같은 다른 것을 알고 싶습니까?
Kilian Foth

@KilianFoth-내가 특히 알고 싶은 것에 대한 정보를 추가했습니다
paj28

1
귀하의 질문은 웹 응용 프로그램에만 국한되지 않으며 데스크톱 응용 프로그램은 정확히 동일한 문제가 발생할 수 있습니다. 일반적인 솔루션 전략은 여기에 설명되어 있습니다 : stackoverflow.com/questions/129329/…
Doc Brown

2
참고로 귀하의 질문에 언급 한 잠금 형식은 " 낙관적 동시성 제어 "라고합니다
TehShrike

장고에 관한 논의 여기
paj28

답변:


17

변경 사항을 기록하기 전에 레코드를 다시 읽고 마지막으로 읽은 이후 변경 사항이 있는지 확인해야합니다. 이 필드를 기준으로 (세분화) 또는 타임 스탬프 (굵은 단위)를 기반으로 수행 할 수 있습니다. 이 점검을 수행하는 동안 레코드에 대한 독점 잠금이 필요합니다. 변경 사항이 없으면 변경 사항을 기록하고 잠금을 해제 할 수 있습니다. 그 동안 레코드가 변경된 경우 트랜잭션을 중단하고 잠금을 해제 한 후 사용자에게 알립니다.


이것은 가장 실용적인 접근법처럼 들립니다. 이것을 구현하는 프레임 워크를 알고 있습니까? 이 체계의 가장 큰 문제점은 간단한 "편집 충돌"메시지가 사용자를 실망 시키지만 변경 세트를 수동 또는 자동으로 병합하는 것은 어렵다는 것입니다.
paj28

불행히도, 나는 이것을 즉시 지원하는 프레임 워크를 모른다. 편집 충돌 오류 메시지가 자주 발생하지 않는 한 좌절감을 느끼지 않을 것이라고 생각합니다. 궁극적으로 타임 스탬프를 확인하거나보다 복잡한 병합 기능을 구현할 때 시스템의 사용자로드에 따라 다릅니다.
Phil

로컬 데이터베이스 복사본에 대해 세분화 된 접근 방식을 사용하는 PC 분산 데이터베이스 제품을 유지 관리했습니다. 한 사용자가 가격을 변경하고 다른 사용자가 설명을 변경 한 경우 문제 없습니다! 실생활에서와 마찬가지로. 두 명의 사용자가 가격을 변경 한 경우-두 번째 사용자는 사과를 받고 변경을 다시 시도합니다. 문제 없어! 데이터가 데이터베이스에 기록되는 순간을 제외하고는 잠금이 필요하지 않습니다. 한 명의 사용자가 변경 사항이 화면에 표시된 상태에서 점심 식사를하고 나중에 제출해도 상관 없습니다. 원격 데이터베이스 변경의 경우 레코드 타임 스탬프를 기반으로합니다.

1
Dataflex에는 "reread ()"라는 함수가 있습니다. 이후 버전에서는 다중 사용자 환경에서 안전했습니다. 실제로 인터리브 된 업데이트를 작동시키는 유일한 방법이었습니다.

SQL Server에서이를 수행하는 방법에 대한 예를 들어 줄 수 있습니까? \
l --''''''---------- '' '' '' '' '' ''

10

나는 두 가지 주요 방법을 보았습니다.

  1. 사용이 숨겨진 입력에서 편집중인 페이지의 마지막 업데이트 시간 소인을 추가하십시오. 타임 스탬프를 커밋 할 때 현재 타임 스탬프를 검사하고 일치하지 않으면 다른 사람이이를 업데이트하여 오류를 반환합니다.

    • 찬성 : 여러 사용자가 페이지의 다른 부분을 편집 할 수 있습니다. 오류 페이지는 두 번째 사용자가 새 페이지에서 변경 사항을 병합 할 수있는 diff 페이지로 이어질 수 있습니다.

    • 단점 : 대규모 동시 편집 중에 많은 노력이 낭비되는 경우가 있습니다.

  2. 사용자가 페이지 잠금을 편집하기 시작하면 적절한 시간 동안 다른 사용자가 편집을 시도하면 오류 페이지가 표시되고 잠금이 만료되거나 첫 번째 사용자가 커밋 할 때까지 기다려야합니다.

    • 찬성 : 편집 노력이 낭비되지 않습니다.

    • 단점 : 파렴치한 사용자가 페이지를 무기한으로 잠글 수 있습니다. 잠금이 만료 된 페이지는 다른 방법으로 처리하지 않는 한 여전히 커밋 할 수 있습니다 (기술 1 사용).


7

낙관적 동시성 제어를 사용하십시오 .

해당 테이블에 versionNumber 또는 versionTimestamp 열을 추가하십시오 (정수가 가장 안전합니다).

사용자 1은 레코드를 읽습니다.

{id:1, status:unimportant, version:5}

사용자 2는 레코드를 읽습니다.

{id:1, status:unimportant, version:5}

사용자 1이 레코드를 저장하면 버전이 증가합니다.

save {id:1, status:important, version:5}
new value {id:1, status:important, version:6}

사용자 2는 읽은 레코드를 저장하려고합니다.

save {id:1, status:unimportant, version:5}
ERROR

Hibernate / JPA는 @Version주석 으로 이것을 자동적으로 할 수 있다

일반적으로 세션에서 어딘가에 읽기 레코드의 상태를 유지해야합니다 (숨겨진 양식 변수보다 안전합니다).


감사합니다 ... @Version에 대해 특히 도움이됩니다. 한 가지 질문 : 세션에 상태를 저장하는 것이 왜 안전한가요? 이 경우 뒤로 버튼을 사용하면 상황이 혼동 될 수 있습니다.
paj28

사용자가 버전 값을 변경할 수 없으므로 세션은 숨겨진 양식 요소보다 안전합니다. 그것이 걱정되지 않는다면 세션에 관한 부분을 무시하십시오
Neil McGuigan

이 기술은이라고 낙관적 오프라인 잠금 그것은 SQLAlchemy의에 뿐만 아니라
paj28

@ paj28-해당 링크 SQLAlchemy는 낙관적 인 오프라인 잠금에 대해 아무것도 가리 키지 않으며 문서에서 찾을 수 없습니다. 더 유용한 링크가 있거나 SQLAlchemy의 사람들을 일반적으로 가리키고 있습니까?
dwanderson

@ dwanderson 나는 그 링크의 버전 카운터 부분을 의미했다.
paj28

1

일부 ORM (Object Relational Mapping) 시스템은 데이터베이스에서로드 된 후 변경된 오브젝트 필드를 감지하고 해당 값만 설정하도록 SQL 업데이트 명령문을 구성합니다. Ruby on Rails 용 ActiveRecord는 그러한 ORM 중 하나입니다.

결과적으로 사용자가 변경하지 않은 필드는 데이터베이스로 전송 된 UPDATE 명령에 포함되지 않습니다. 다른 필드를 동시에 업데이트하는 사람들은 서로의 변경 사항을 덮어 쓰지 않습니다.

사용중인 프로그래밍 언어에 따라 사용 가능한 ORM을 조사하고 응용 프로그램에서 "더티"라고 표시된 데이터베이스의 열만 업데이트하는지 확인하십시오.


안녕 그렉 불행히도 이것은 이런 종류의 경쟁 조건에는 도움이되지 않습니다. 내 원래 예를 고려하면 Alice가 저장하면 ORM은 업데이트를 원하지 않더라도 가격 열이 더티로 표시되어 업데이트됩니다.
paj28

1
@ paj28 Greg의 답변에서 핵심은 " 사용자가 변경하지 않은 필드 "입니다. Alice는 가격을 변경하지 않았으므로 ORM은 "price"값을 데이터베이스에 저장하지 않습니다.
로스 패터슨 1

@RossPatterson-ORM은 사용자가 변경 한 필드와 브라우저에서 오래된 데이터의 차이를 어떻게 알 수 있습니까? 적어도 추가 추적을 수행하지 않으면 그렇지 않습니다. 이러한 추적을 포함하도록 Greg의 답변을 편집하거나 다른 답변을 제출하려는 경우 도움이됩니다.
paj28

@ paj28 시스템의 일부는 사용자가 무엇을했는지 알아야하고 사용자가 변경 한 내용 만 저장해야합니다. 사용자가 가격을 변경 한 후 다시 변경 한 후 제출 한 경우 "사용자가 변경 한 것"으로 간주되지 않습니다. 이 수준의 동시성 제어가 필요한 시스템이있는 경우이를 이와 같이 구축해야합니다. 그렇지 않다면 아닙니다.

@nocomprende-이 답변에서 말하는 것처럼 ORM은 아닙니다.
paj28
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.