2010-04-07 3 views
25

DBI를 통해 연결하는 병렬 Perl 프로세스에 의해 작은 방식으로 지속적으로 업데이트되는 약 5,000,000 개의 행이있는 MySQL 테이블이 있습니다. 이 테이블에는 약 10 개의 열과 여러 개의 인덱스가 있습니다.MySQL 오류 "잠금을 시도 할 때 교착 상태가 발견되어 트랜잭션을 다시 시작하십시오."

UPDATE file_table SET a_lock = 'process-1234' WHERE param1 = 'X' AND param2 = 'Y' AND param3 = 'Z' LIMIT 47 

이 오류 만 가끔 트리거 :

DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction at Db.pm line 276. 

오류를 트리거 SQL 문이 같은 것입니다 :

한 매우 일반적인 작업은 종종 다음과 같은 오류를 일으킨다. 나는 1 % 이하의 전화로 추정한다. 그러나 작은 테이블을 사용한 적이 없었고 데이터베이스가 커짐에 따라 점점 더 보편화되었습니다.

file_table의 a_lock 필드를 사용하여 실행중인 거의 동일한 네 개의 프로세스가 동일한 행에서 작동하지 않도록주의하십시오. 이 제한은 작업을 작은 덩어리로 나누기 위해 고안되었습니다.

MySQL 또는 DBD :: mysql에 대한 튜닝을 많이하지 않았습니다. MySQL은 표준 솔라리스 배포하고, 다음과 같이 데이터베이스 연결이 설정됩니다 : 나는 여러 다른 사람들이 유사한 오류를보고 있고 이것이 진정한 교착 상태가 될 수 있음을 온라인으로 보았다

my $dsn = "DBI:mysql:database=" . $DbConfig::database . ";host=${DbConfig::hostname};port=${DbConfig::port}"; 
my $dbh = DBI->connect($dsn, $DbConfig::username, $DbConfig::password, { RaiseError => 1, AutoCommit => 1 }) or die $DBI::errstr; 

.

나는 두 가지 질문이 있습니다

  1. 정확히 어떤 내 상황이 위의 오류를 일으키는 대해?

  2. 주파수를 줄이거 나 줄일 수있는 간단한 방법이 있습니까? 예를 들어, "Db.pm 라인 276에서 트랜잭션 재시작"에 대해 정확히 어떻게해야합니까?

미리 감사드립니다.

답변

61

당신은 이노 또는 행 수준 트랜잭션 RDBMS를 사용하는 경우, 어떤 쓰기 트랜잭션도 완벽하게 정상적인 상황에서 교착 상태가 발생할 수있는 가능성이 있습니다. 큰 테이블, 큰 쓰기 및 긴 트랜잭션 블록은 종종 교착 상태가 발생할 가능성을 높입니다. 귀하의 상황에서, 아마도 이것들의 조합 일 것입니다.

진정한 교착 상태를 처리하는 유일한 방법은 예상치 못한 코드를 작성하는 것입니다. 데이터베이스 코드가 잘 작성된 경우 일반적으로 그렇게 어렵지 않습니다. 종종 쿼리 실행 논리 주위에 try/catch을 넣고 오류가 발생할 때 교착 상태가 있는지 확인할 수 있습니다. 만약 당신이 하나 잡으면, 정상적인 일은 실패한 쿼리를 다시 실행하려고 시도하는 것입니다.

MySQL 설명서에서 this page을 읽어 보시기 바랍니다. 교착 상태에 대처하고 빈도를 줄이기 위해 할 일 목록이 있습니다.

+2

그때 우리가 잡을 필요가 오류 코드는 무엇입니까? 1205만으로도 충분합니까? http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html에는 900 개 이상의 오류 코드가 있습니다. 귀하의 시도/포획 제안을위한 적절한 솔루션을 구현하기 위해 우리가 알아야 할 모든 코드를 어떻게 알 수 있습니까? – Pacerier

+0

InnoDB 또는 행 수준의 트랜잭션 RDBMS 이외의 다른 것이 이러한 문제점을 가지고 있지 않음을 의미합니까? –

5

을 사용하여 삽입 전에 고유성 검사를 수행하면 innodb_locks_unsafe_for_binlog 옵션을 설정하지 않으면 모든 경쟁 조건에 대해 교착 상태가 발생합니다. 고유성을 검사하는 교착 상태가없는 방법은 INSERT IGNORE을 사용하여 고유 인덱스가있는 테이블에 맹목적으로 행을 삽입 한 다음 영향을받은 행 수를 확인하는 것입니다.대답은, 그러나 펄 문서 올바른 OFF

#
+0

다중 스레드 환경에서 ActiveRecord 연관을 저장하는 것과 관련된 모든 문제를 해결했습니다. – lightyrs

+2

'innodb_locks_unsafe_for_binlog'를 유효하게하면 (자), 갭 락이 무효가 되어도, 다른 세션이 갭에 새로운 행을 삽입 할 수 있기 때문에, 팬텀 문제가 발생할 가능성이 있습니다. – shivam

9

을 -
0 ON - my.cnf 파일

때에 innodb_locks_unsafe_for_binlog = 1

#

1 선 아래

추가 교착 상태를 처리하는 방법에 대해서는 약간 희소하고 아마도 PrintError, RaiseError 및 HandleError와 혼동을 일으킬 수 있습니다. 옵션. HandleError를 사용하는 대신 Print 및 Raise를 사용하고 Try : Tiny와 같은 코드를 사용하여 오류를 확인하는 것과 같은 것을 사용하는 것 같습니다. 아래 코드는 db 코드가 while 루프 안에 있고 3 초마다 오류가 발생한 SQL 문을 다시 실행하는 예제입니다. catch 블록은 특정 오류 메시지 인 $ _를 가져옵니다. 나는이 함수를 $ _를 호스트 에러에 대해 검사하고 코드가 계속되면 (루프가 끊어지면) 1을 반환하는 핸들러 함수 "dbi_err_handler"에 전달한다.

$sth = $dbh->prepare($strsql); 
my $db_res=0; 
while($db_res==0) 
{ 
    $db_res=1; 
    try{$sth->execute($param1,$param2);} 
    catch 
    { 
     print "caught $_ in insertion to hd_item_upc for upc $upc\n"; 
     $db_res=dbi_err_handler($_); 
     if($db_res==0){sleep 3;} 
    } 
} 

dbi_err_handler는 다음과 같은 최소한 있어야한다 : 당신은 당신이 처리하고 싶은 다른 오류를 당신이 다시 실행하거나 계속할지 여부에 따라 설정 $의 RETVAL .. 포함해야

sub dbi_err_handler 
{ 
    my($message) = @_; 
    if($message=~ m/DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction/) 
    { 
     $caught=1; 
     $retval=0; # we'll check this value and sleep/re-execute if necessary 
    } 
    return $retval; 
} 

희망이 도움이 누군가 -

0

교착 상태 예외 상황에서 쿼리를 다시 시도한다는 생각은 좋지만 mysql 쿼리는 잠금이 해제 될 때까지 기다릴 것이므로 매우 느릴 수 있습니다. 그리고 교착 상태가 발생하면 mysql은 교착 상태가 있는지를 알아 내려고하고 교착 상태가 있음을 알아 낸 후에도 교착 상황에서 벗어나기 위해 스레드를 걷어차 기 전에 잠시 기다린다.

이 상황에 직면했을 때 버그로 인해 MySQL의 잠금 메커니즘이 실패했기 때문에 자신의 코드에서 잠금을 구현하는 것이 었습니다. 그래서 내 자바 코드에서 내 자신의 행 수준 잠금을 구현 :

private HashMap<String, Object> rowIdToRowLockMap = new HashMap<String, Object>(); 
private final Object hashmapLock = new Object(); 
public void handleShortCode(Integer rowId) 
{ 
    Object lock = null; 
    synchronized(hashmapLock) 
    { 
     lock = rowIdToRowLockMap.get(rowId); 
     if (lock == null) 
     { 
      rowIdToRowLockMap.put(rowId, lock = new Object()); 
     } 
    } 
    synchronized (lock) 
    { 
     // Execute your queries on row by row id 
    } 
} 
+4

유감스럽게도 대부분의 사용자는 여러 대의 컴퓨터 또는 단일 MySQL 인스턴스로 데이터를 덤프하는 프로세스를 처리하고 있습니다. 응용 프로그램의 행 수준 잠금은 대부분의 사용자에게 단순히 옵션이 아닙니다. – dgtized

관련 문제