2017-12-14 9 views
0

MySQL을 기본 격리 수준 (반복 가능 읽기)에서 실행 중입니다.중복 키 오류 삽입 후 SELECT가 실패 함

파이썬 코드는 두 개의 동시 프로세스 내에서 동시에 실행할 수있는 트랜잭션을 관리하기 위해 sqlalchemy를 사용합니다.

c.begin(); 
# this SELECT establishes the transaction snapshot from which all other SELECTs will read data 
c.execute("SELECT something FROM sometable;") 
try: 
    c.execute("INSERT INTO othertable (unique_key) VALUES (1)") 
except sqlalchemy.exc.IntegrityError as e: 
    code, msg = e.orig 
    if code != 1062: 
    raise 
    # duplicate key: another transaction commited the above INSERT so I can't rely on LAST_INSERT_ID 
    rows = c.execute("SELECT * FROM table WHERE unique_key=1") 
    inserted_id = None 
    for id, in rows: 
    inserted_id = id 
    break 
    assert inserted_id is not None 
else: 
    inserted_id = c.last_insert_id() 
c.commit() 

정확한 코드

분명히 좀 더 복잡한 더 많은 쿼리하지만 문제의 핵심은이 코드가 자주 예외 처리기에서 어설 명중이다.

이유는이 트랜잭션이 다른 프로세스에서 동시에 실행되는 경우가 많기 때문입니다. 첫 번째 SELECT에 의해 설정된 DB 스냅 샷에 새로 삽입 된 행이 들어 있지 않기 때문에 중복 키 예외에서 트리거되는 SELECT가 실패합니다 첫 번째 SELECT 후 INSERT 전에 다른 트랜잭션에 의해 삽입되었습니다.

이제 FOR UPDATE를 사용하도록 SELECT를 변경할 수 있다는 사실을 알고 스냅 샷에 구멍을내어 삽입 된 실제 값을 읽습니다.

그러나 나는 이것이 꽤 많은 장소에서 이것을 뿌려야 할 것이기 때문에 이것을 꺼려하고 있으며, 나는 이것을 비교적 연약한 것으로 봅니다.

이 concurent 삽입물을 처리하는 데있어 좀 더 견고하고 표준이 낮고 추악하지 않은 방법이 있습니다 (처리해야하는 것은 사실입니다). 인공 업데이 트를 추가

답변

0

의 거래 스냅 샷을 업데이트하기 위해 MySQL을 강제로 충분한 것으로 나타납니다

c.begin(); 
# this SELECT establishes the transaction snapshot from which all other SELECTs will read data 
c.execute("SELECT something FROM sometable;") 
try: 
    c.execute("INSERT INTO othertable (unique_key) VALUES (1)") 
except sqlalchemy.exc.IntegrityError as e: 
    code, msg = e.orig 
    if code != 1062: 
    raise 
    # duplicate key: another transaction commited the above INSERT so I can't rely on LAST_INSERT_ID 
    # ARTIFICAL UPDATE TO FORCE MYSQL TO UPDATE ITS SNAPSHOT 
    c.execute("UPDATE table SET value = something WHERE unique_key=1") 
    rows = c.execute("SELECT * FROM table WHERE unique_key=1") 
    inserted_id = None 
    for id, in rows: 
    inserted_id = id 
    break 
    assert inserted_id is not None 
else: 
    inserted_id = c.last_insert_id() 
c.commit() 

참고 :

  1. MariaDB 10.2.9이 어떤 UPDATE 문에 만족하실 것 같습니다 이 행 (행 값을 변경하지 않는 값 업데이트 포함)을 사용하여 스냅 샷 업데이트를 트리거합니다.
  2. MySQL 5.6.28은 UPDATE가 변경되지 않는 한 스냅 샷 업데이트를 무시합니다 행의 값은 어떻게 든