2015-02-06 3 views
6

는 업데이트 :포스트 그레스 9.3 : 간단한 INSERT와 Sharelock 문제가

아래의 잠재적 인 솔루션 나는 데이터베이스에 밀어하기 위해 노력하고있어 키/값 쌍으로 구성된 구성 파일의 큰 코퍼스 있습니다. 많은 키와 값이 구성 파일에서 반복되므로 3 개의 테이블을 사용하여 데이터를 저장합니다. 모든 고유 한 키 값 하나, 모든 고유 한 쌍 값 하나, 각 파일의 모든 키/값 쌍 나열.

문제 : 원시 데이터를 데이터베이스에 추가하기 위해 여러 개의 동시 프로세스 (따라서 연결)를 사용하고 있습니다. 불행히도 키와 값 테이블에 값을 추가하려고 할 때 많은 교착 상태가 발생합니다. 나는 (아래 그림 참조) 데이터를 삽입하는 몇 가지 방법을 시도했지만 항상 누군가가 정확히 원인이 될 수 되거 수 있는지 궁금

TransactionRollbackError: deadlock detected
DETAIL: Process 26755 waits for ShareLock on transaction 689456; blocked by process 26754. Process 26754 waits for ShareLock on transaction 689467; blocked by process 26755.

는 "감지 된 교착 상태"오류와 끝까지 한 이러한 교착 상태는 문제 해결에 도움이 될 수 있습니다. 아래에 나열된 SQL 문을 살펴보면 공통 종속성이있는 이유를 실제로 볼 수 없습니다.

읽어 주셔서 감사합니다.

예 설정 파일 :

example_key this_is_the_value 
other_example other_value 
third example yet_another_value 

테이블 정의 :

CREATE TABLE keys (
     id SERIAL PRIMARY KEY, 
     hash UUID UNIQUE NOT NULL, 
     key TEXT); 

    CREATE TABLE values (
     id SERIAL PRIMARY KEY, 
     hash UUID UNIQUE NOT NULL, 
     key TEXT); 

    CREATE TABLE keyvalue_pairs (
     id SERIAL PRIMARY KEY, 
     file_id INTEGER REFERENCES filenames, 
     key_id INTEGER REFERENCES keys, 
     value_id INTEGER REFERENCES values); 

SQL 문 : 처음

나는 예외를 피하기 위해이 문을 사용하려고했다 :삽입 된 해시 값이 고유하지 않기 때문에 내가 던진 예외가 있다면, 나는 세이브 포인트를 사용하여, 두 경우 모두

INSERT INTO keys (hash, key) 
     VALUES ('hash_value', 'key_value') 
     RETURNING id; 
  • :
    WITH s AS (
         SELECT id, hash, key FROM keys 
          WHERE hash = 'hash_value'; 
        ), i AS (
         INSERT INTO keys (hash, key) 
         SELECT 'hash_value', 'key_value' 
         WHERE NOT EXISTS (SELECT 1 FROM s) 
         returning id, hash, key 
        ) 
        SELECT id, hash, key FROM i 
        UNION ALL 
        SELECT id, hash, key FROM s; 
    

    하지만이처럼 간단 심지어 뭔가은 교착 상태의 원인 변경 사항을 롤백하고 다른 이니셜을 선택하기 만하면됩니다.

    :
  • 나는 키의 일부로서, 독특한 필드에 대한 해시를 사용하고 값 해요
세이브 포인트로 파이썬 코드 (사용 psycopg2)의

전체 예를을 색인 너무 긴

key_value = 'this_key' hash_val = generate_uuid(value) try: cursor.execute( ''' SAVEPOINT duplicate_hash_savepoint; INSERT INTO keys (hash, key) VALUES (%s, %s) RETURNING id; ''' (hash_val, key_value) ) result = cursor.fetchone()[0] cursor.execute('''RELEASE SAVEPOINT duplicate_hash_savepoint''') return result except psycopg2.IntegrityError as e: cursor.execute( ''' ROLLBACK TO SAVEPOINT duplicate_hash_savepoint; ''' ) #TODO: Should ensure that values match and this isn't just #a hash collision cursor.execute( ''' SELECT id FROM keys WHERE hash=%s LIMIT 1; ''' (hash_val,) ) return cursor.fetchone()[0] 

업데이트 : 그래서 내가 another stackexchange site:

에 내가 힌트 생각3210 구체적으로 : 나는 아직도 공동 의존성이 어디 있는지 정확히 모르겠어요 동안

UPDATE, DELETE, SELECT FOR UPDATE, and SELECT FOR SHARE commands behave the same as SELECT in terms of searching for target rows: they will only find target rows that were committed as of the command start time1. However, such a target row might have already been updated (or deleted or locked) by another concurrent transaction by the time it is found. In this case, the would-be updater will wait for the first updating transaction to commit or roll back (if it is still in progress). If the first updater rolls back, then its effects are negated and the second updater can proceed with updating the originally found row. If the first updater commits, the second updater will ignore the row if the first updater deleted it2, otherwise it will attempt to apply its operation to the updated version of the row.

, 아마 이런 일이 발생할 것입니다 커밋하지 않고 키/값 쌍의 큰 숫자를 처리 할 것으로 보인다. 물론 각 개별 구성 파일을 추가 한 후 커밋하면 교착 상태가 발생하지 않습니다.

답변

7

그것은이 상황에있어 다음과 같습니다

  1. 에 삽입 할 테이블의 기본 키 (또는 어떤 종류의 고유 인덱스 (들))이있다. 행이 삽입되어
  2. (기본 키와 관련하여) 임의의 순서로 제공 삽입
  3. 행 (각 직후 투입 대조적으로) 그 테이블에
  4. 여러 인서트는 하나의 트랜잭션 내에서 수행 동시 트랜잭션. 각각 거래를 시작했다, 두 개의 세션이 가정

    :

이 상황은 교착 상태에 대해 다음 기회를 만듭니다.

  1. 세션 # 1 : PK 'A'와 삽입 행
  2. 세션 # 2 : PK 'B'와 삽입 행
  3. 세션 # 1 : PK 'B'로 행을 삽입 할 => 세션 # 1은 세션 # 2가 커밋되거나 롤백 될 때까지 대기합니다.
  4. 세션 # 2 : PK 'A'와 함께 행 삽입 시도 => 세션 # 2는 세션 # 1을 기다립니다.

는 잠시 후, 교착 상태 검출기는 두 세션은 이제 서로를 기다리고있는 것을 알고 가져오고 치명적인 교착 상태와 그들 중 하나가 오류를 발견 종료합니다.

이 시나리오에서는 가장 간단한 해결책은 테이블에 새 행을 삽입하기 전에 새 항목을 삽입 한 후 COMMIT하는 것입니다.

+0

또 다른 방법은 값을 항상 같은 순서로 삽입하는 것입니다. –