2011-03-08 1 views
7

외부 시스템과의 인터페이스를위한 복잡한 기본 키와 내부 사용을위한 빠르고 작은 불투명 기본 키가있는 시스템이 있습니다. 예를 들어, 외부 키는 (주어진 이름 (varchar), 성 (varchar), 우편 번호 (char)와 같은) 복합 값일 수 있으며 내부 키는 정수 ("고객 ID")가 될 수 있습니다. 내가 외부 키를 사용하여 들어오는 요청을 수신 할 때일반 SQL에서 새 행을 동시에 검색 (선택) 또는 작성 (삽입)

, 나는 내부 키를 볼 필요 - 여기에 까다로운 부분입니다 - 이미 주어진 외부 ID에 대한이없는 경우 새로운 내부 키를 할당 .

분명히 한 번에 하나의 클라이언트 만 데이터베이스에 대화하는 것이면 좋습니다. SELECT customer_id FROM customers WHERE given_name = 'foo' AND ..., 그 다음 INSERT INTO customers VALUES (...) 값을 찾지 못했습니다. 그러나 외부 시스템에서 동시에 들어오는 요청이 많고 이전에 전례가없는 고객이 한꺼번에 많은 요청을받을 수있는 경우 여러 클라이언트가 새로운 행을 시도 할 수있는 경쟁 조건이 있습니다.

기존 행을 수정하는 것이 쉽습니다. 단순히 SELECT FOR UPDATE, 먼저 UPDATE을 수행하기 전에 적절한 행 수준 잠금을 획득하십시오. 그러나이 경우 행이 아직 존재하지 않기 때문에 잠글 수있는 행이 없습니다!

지금까지 몇 가지 솔루션을 마련했지만, 그들 각각은 꽤 중요한 문제가 있습니다

  1. 캐치 INSERT에 오류가 상단에서 전체 트랜잭션을 다시 시도하십시오. 이것은 거래가 12 명의 고객을 포함하는 경우, 특히 들어오는 데이터가 매번 다른 고객의 주문에 대해 잠재적으로 이야기하고있는 경우 문제가됩니다. 매번 다른 고객에게 충돌이 발생하는 상호 회귀 교착 상태 루프에 걸릴 수 있습니다. 재 시도 시도 사이의 지수 대기 시간을 사용하여이를 완화 할 수 있지만 충돌을 처리하는 데 시간이 오래 걸리고 비용이 많이 듭니다. 또한 모든 것이 다시 시작될 수 있어야하므로 응용 프로그램 코드가 상당히 복잡해집니다.
  2. 세이브 포인트를 사용하십시오. SELECT 전에 세이브 포인트를 시작하고 INSERT에서 오류를 catch 한 다음 세이브 포인트 및 SELECT으로 다시 롤백하십시오. 세이브 포인트는 완전히 이식 가능하지 않으며, 의미와 기능이 데이터베이스간에 조금씩 다르다. 내가 알아 차 렸던 가장 큰 차이는 때로는 둥지처럼 보이기도하고 때로는 그렇지 않기 때문에 피할 수 있다면 좋을 것입니다. 이것은 막연한 인상 일뿐입니다. 정확하지 않습니까? 저장 점은 표준화 되었는가, 아니면 적어도 실질적으로 일관성이 있는가? 또한 저장 지점을 사용하면 트랜잭션에서 작업을 병렬로 수행하기가 어려워집니다. 롤백 할 작업의 양을 정확히 알 수 없기 때문에 저장해야 할 수도 있습니다.
  3. LOCK 문 (oraclemysqlpostgres)을 사용하는 테이블 수준 잠금과 같은 일부 글로벌 잠금을 가져옵니다. 이것은 분명히 이러한 작업을 느리게하고 잠금 경합이 많이 발생하므로이를 피하는 것이 좋습니다.
  4. 보다 세부적이지만 데이터베이스 관련 잠금을 획득합니다. 나는 다른 데이터베이스 (이 함수는 심지어 "pg_"로 시작하는 경우도)에서 지원되지 않으므로 Postgres's way of doing this에만 익숙하므로 이식성 문제가 있습니다. 또한 포스트 그레스의 방법은 내가 키를 정수형으로 변환해야 할 필요가 있는데, 이는 깔끔하게 적합하지 않을 수 있습니다. 가상의 물건에 대한 자물쇠를 잡는 좋은 방법이 있습니까?

이것은 데이터베이스에서 공통적 인 동시성 문제가 될 것으로 보이지만 많은 리소스를 찾을 수 없었습니다. 아마 내가 정규 표현을 모르기 때문에. 태그가 추가 된 데이터베이스 중 일부에서 간단한 구문을 사용하여이 작업을 수행 할 수 있습니까?

+1

upsert/merge 문? –

+0

customerid가 외부 키의 일부인 경우 문제가 표시되지 않습니다. – dkretz

+1

질문이 ** 단지 ** MySql이면 ** INSERT ... ON DUPLICATE KEY를 사용하는 것이 좋습니다. (선택적으로'customer_id = LAST_INSERT_ID (customer_id) '를 사용하여 행 customer_id를 검색) –

답변

0

불투명 한 기본 키를 생성하는 WRT에는 여러 가지 옵션이 있습니다. 예를 들어, guid 또는 (적어도 Oracle과 함께) 시퀀스 테이블을 사용하십시오. WRT 외부 키가 고유한지 확인하려면 열에 고유 제한 조건을 적용하십시오. 키가있어 삽입이 실패하면 반입을 다시 시도하십시오. 저장 프로 시저를 사용하여 왕복 횟수를 줄이고 성능을 향상시킬 수 있습니다.

3

INSERT IGNORE를 사용할 수없는 이유는 명확하지 않습니다. 오류없이 실행되며 삽입이 발생했는지 확인할 수 있습니다 (수정 된 레코드). 삽입이 "실패"하면 키가 이미 존재하고 SELECT를 수행 할 수 있음을 알 수 있습니다. 먼저 INSERT를 수행 한 다음 SELECT를 수행 할 수 있습니다.

MySQL을 사용하는 경우 트랜잭션을 지원하는 InnoDB를 사용하십시오. 그러면 롤백이 더 쉬워집니다.

+1

INSERT IGNORE는 MySQL과 관련이있는 것으로 보입니다.하지만 원하는대로 할 수 있습니다. 좀 더 휴대용 대안이 있습니까? – Glyph

1

주요 다중 고객 트랜잭션 이전 및 이후에 자동 커밋 모드로 각 고객의 "조회 또는 작성"작업을 수행하십시오.

관련 문제