문제

MySQL에서 경쟁 조건을 어떻게 중지합니까?당면한 문제는 간단한 알고리즘으로 인해 발생합니다.

  1. 테이블에서 행을 선택
  2. 존재하지 않으면 삽입하십시오.

그런 다음 중복 행이 생기거나 고유/기본 키를 통해 방지하면 오류가 발생합니다.

일반적으로 여기서는 트랜잭션이 도움이 된다고 생각하지만 행이 존재하지 않기 때문에 트랜잭션이 실제로 도움이 되지 않습니다(또는 제가 뭔가를 놓치고 있는 걸까요?).

LOCK TABLE은 과도한 것처럼 들립니다. 특히 테이블이 초당 여러 번 업데이트되는 경우 더욱 그렇습니다.

내가 생각할 수 있는 유일한 다른 해결책은 모든 다른 ID에 대해 GET_LOCK()이지만 더 좋은 방법은 없을까요?여기서도 확장성 문제는 없나요?또한 모든 테이블에 대해 이 작업을 수행하는 것은 약간 부자연스럽게 들립니다. 이는 동시성이 높은 데이터베이스에서 매우 일반적인 문제처럼 들리기 때문입니다.

도움이 되었습니까?

해결책

당신이 원하는 것은 잠금 테이블

또는 그것이 과도한 것처럼 보인다면 무시하십시오 행이 실제로 삽입되었는지 확인했습니다.

무시 키워드를 사용하는 경우 삽입 문을 실행하는 동안 발생하는 오류는 대신 경고로 처리됩니다.

다른 팁

ID 열에 고유 한 인덱스가 있어야하므로 반복 된 삽입물은 맹목적으로 다시 수락하는 대신 오류를 트리거 할 것입니다.

ID를 기본 키로 정의하거나 고유 한 색인 자체를 사용하여 수행 할 수 있습니다.

당신이 물어봐야 할 첫 번째 질문은 왜 똑같은 작업을하는 스레드가 왜 있습니까? 왜 정확히 같은 행을 삽입해야합니까?

그에 대한 답을 얻은 후에는 오류를 무시하는 것이 가장 성능있는 솔루션이 될 것이라고 생각하지만 두 가지 접근 방식 (get_lock v/s는 오류를 무시하고 직접 확인합니다.

내가 아는 다른 방법은 없습니다. 오류를 피하고 싶은 이유는 무엇입니까? 다른 유형의 오류가 발생한 경우에도 여전히 케이스를 코딩해야합니다.

Staticsan은 트랜잭션이 도움이되지만 일반적으로 암시 된 바와 같이 두 개의 인서트가 다른 스레드에 의해 실행되면 묵시적 트랜잭션 내부에 있으며 데이터베이스의 일관된 견해를 보게됩니다.

전체 테이블을 잠그는 것은 실제로 과잉입니다.원하는 효과를 얻으려면 문헌에서 "조건자 잠금"이라고 부르는 것이 필요합니다.학술 연구가 출판되는 종이에 인쇄된 것 외에는 누구도 그것을 본 적이 없습니다.차선책은 데이터에 대한 "액세스 경로"를 잠그는 것입니다(일부 DBMS의 경우:"페이지 잠금").

일부 비SQL 시스템에서는 하나의 명령문으로 (1)과 (2)를 모두 수행할 수 있습니다. 즉, (1)과 (2) 사이에서 실행 스레드를 일시 중단하는 OS에서 발생하는 잠재적인 경쟁 조건이 완전히 제거되었습니다.

그럼에도 불구하고 조건자 잠금이 없는 경우 이러한 시스템은 여전히 ​​일종의 잠금 체계에 의존해야 하며 잠금의 "세분성"(/"범위")이 더 세밀할수록 동시성에 더 좋습니다.

(결론은 다음과 같습니다.일부 DBMS, 특히 비용을 지불할 필요가 없는 DBMS는 실제로 "전체 테이블"보다 더 미세한 잠금 세분성을 제공하지 않습니다.)

기술적 인 수준에서는 거래가 거래 할 때까지 다른 스레드가 새 행을 볼 수 없기 때문에 거래가 여기에서 도움이됩니다.

그러나 실제로는 그렇지 않습니다 해결하다 문제 - 그것은 단지 움직입니다 그것. 신청서는 이제 커밋이 실패하는지 확인하고해야 할 일을 결정해야합니다. 나는 일반적으로 당신이 한 일을 롤백하고 이제 행이 보이기 때문에 거래를 다시 시작합니다. 이것이 거래 기반 프로그래머가 작동하는 방식입니다.

나는 같은 문제에 부딪쳐 잠시 동안 그물을 검색했다 :)

마침내 나는 방법과 유사한 솔루션을 생각해 냈습니다. 공유 (임시) 디렉토리에서 파일 시스템 객체 생성을 위해 임시 파일을 안전하게 열기 위해 :

$exists = $success = false;
do{
 $exists = check();// select a row in the table 
 if (!$exists)
  $success = create_record();
  if ($success){
   $exists = true;
  }else if ($success != ERROR_DUP_ROW){
    log_error("failed to create row not 'coz DUP_ROW!");
    break;
  }else{
    //probably other process has already created the record,
    //so try check again if exists
  }
}while(!$exists)

두려워하지 마십시오 바쁜 루프 - 일반적으로 한두 번 실행됩니다.

테이블에 고유 한 인덱스를 넣어서 중복 행을 매우 방지합니다. 그것은 자물쇠 나 거래와 관련이 없습니다.

삽입이 중복이기 때문에 실패했는지 신경 쓰나요? 실패한 경우 알림을 받아야합니까? 아니면 행이 삽입 된 모든 것이 중요하며, 누가 또는 얼마나 많은 복제 삽입물이 실패했는지는 중요하지 않습니까?

신경 쓰지 않으면 필요한 것은 INSERT IGNORE. 거래 또는 테이블 잠금에 대해 전혀 생각할 필요가 없습니다.

InnoDB에는 자동으로 행 레벨 잠금이 있지만 업데이트 및 삭제에만 적용됩니다. 삽입물에는 적용되지 않는 것이 좋습니다. 아직 존재하지 않는 것을 잠글 수 없습니다!

당신은 명시 적으로 할 수 있습니다 LOCK 전체 테이블. 그러나 당신의 목적이 복제를 방지하는 것이라면, 당신은 그것을 잘못하고 있습니다. 다시, 고유 한 색인을 사용하십시오.

변경해야 할 일련의 변경 사항이 있고 모든 또는 전혀없는 결과 (또는 더 큰 전 또는 전혀없는 결과 내에서 일련의 모든 또는 전혀없는 결과를 원한다면 트랜잭션 및 저장 포인트를 사용하십시오. 그런 다음 사용하십시오 ROLLBACK 또는 ROLLBACK TO SAVEPOINT *savepoint_name* 삭제, 업데이트를 포함하여 변경 사항을 취소합니다 그리고 삽입.

LOCK 테이블은 거래를 대체하지는 않지만 거래를 지원하지 않는 MyISAM 테이블을 사용한 유일한 옵션입니다. 로우 레벨 레벨 잠금으로 인해 충분하지 않은 경우 InnoDB 테이블과 함께 사용할 수도 있습니다. 보다 이 페이지 잠금 테이블 문의 트랜잭션 사용에 대한 자세한 내용은

비슷한 문제가 있습니다. 대부분의 상황에서 고유 한 Ticket_ID 값이 있어야하는 테이블이 있지만, 복제물이있는 경우가 있습니다. 최고의 디자인은 아니지만 그것이 바로 그 것입니다.

  1. 사용자 A 티켓이 예약되어 있는지 확인하기위한 수표,
  2. 사용자 B는 티켓이 예약되어 있는지 확인합니다.
  3. 사용자 B는 해당 티켓의 테이블에 '예약 된'레코드를 삽입합니다.
  4. 사용자 A '예약 된'레코드를 해당 티켓의 테이블에 삽입합니다.
  5. 사용자 B 중복을 확인 하시겠습니까? 예, 내 레코드가 더 새롭습니까? 네, 그대로 두십시오
  6. 사용자 A 중복 수표? 예, 내 레코드가 더 새롭습니까? 아니요, 삭제하십시오

사용자 B는 티켓을 예약했으며, 사용자 A는 다른 사람이 티켓을 가져 왔다는 보고서를보고했습니다.

내 인스턴스의 핵심은 타이 브레이커가 필요하다는 것입니다. 제 경우에는 행의 자동 증가 ID입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top