MySQL / InnoDB에서 트랜잭션 시간 제한 설정


11

이것은 이 관련 질문 에서 나왔습니다 . 사소한 경우 (둘 다 단일 행에서만 작동하는 경우)에 두 트랜잭션을 순차적으로 발생시키는 방법을 알고 싶었습니다. SELECT ... FOR UPDATE두 트랜잭션의 첫 번째 줄로 사용하는 대답 이 있지만 문제가 발생합니다. 첫 번째 트랜잭션이 커밋되거나 롤백되지 않으면 두 번째 트랜잭션이 무기한 차단됩니다. innodb_lock_wait_timeout변수는 두 번째 트랜잭션을 만들려고 노력하는 클라이언트가 "죄송합니다 다시 시도하십시오"말 것입니다 ...하지만 지금까지 내가 말할 수있는, 그들이 다음에 서버를 다시 부팅 할 때까지 다시 시도 할 것입니다 후 (초)을 설정합니다. 그래서:

  1. ROLLBACK거래가 영원히 진행 되는 경우 반드시 강제 할 수있는 방법이 있어야 합니까? 그런 트랜잭션을 죽이기 위해 데몬을 사용해야한다면, 그러한 데몬은 어떻게 생겼을까 요?
  2. 연결이 트랜잭션에 의해 종료 wait_timeout되거나 interactive_timeout중간 트랜잭션 인 경우 트랜잭션이 롤백됩니까? 콘솔에서 이것을 테스트하는 방법이 있습니까?

설명 : innodb_lock_wait_timeout트랜잭션이 잠금이 해제 될 때까지 기다리는 시간 (초)을 설정합니다. 내가 원하는 것은 잠금을 해제 하는 방법입니다 .

업데이트 1 : 다음 innodb_lock_wait_timeout은 두 번째 트랜잭션이 첫 번째 트랜잭션에 의해 차단되지 않는 이유를 보여주는 간단한 예입니다 .

START TRANSACTION;
SELECT SLEEP(55);
COMMIT;

기본 설정 인 innodb_lock_wait_timeout = 50이 거래는 55 초 후에 오류없이 완료됩니다. 그리고 줄 UPDATE앞에 before 를 추가 하고 같은 행 SLEEP을 시도하는 다른 클라이언트에서 두 번째 트랜잭션을 시작하면 SELECT ... FOR UPDATE잠자기 트랜잭션이 아닌 두 번째 트랜잭션입니다.

내가 찾고있는 것은이 거래의 편안한 파자마를 끝내는 방법입니다.

업데이트 2 : 위의 예가 얼마나 현실적인 지에 대한 hobodave의 우려에 부응하여 대체 시나리오는 다음과 같습니다. DBA가 라이브 서버에 연결하여 실행됩니다.

START TRANSACTION
SELECT ... FOR UPDATE

여기서 두 번째 줄은 응용 프로그램이 자주 쓰는 행을 잠급니다. 그런 다음 DBA가 중단되고 사라져 트랜잭션 종료를 잊어 버립니다. 행이 잠금 해제 될 때까지 응용 프로그램이 중지됩니다. 이 실수로 인해 응용 프로그램이 멈추는 시간을 최소화하고 싶습니다.


초기 질문과 완전히 충돌하도록 질문을 방금 업데이트했습니다. 당신은 상태 굵은 글씨로 "첫 번째 트랜잭션 커밋하거나, 다음 두 번째 트랜잭션이 무기한 차단됩니다 롤백 결코됩니다." 최신 업데이트로이를 반증합니다. "잠자기 트랜잭션이 아닌 두 번째 트랜잭션입니다." -진심으로?!
hobodave

내 문구가 더 명확하다고 생각합니다. "무한 차단"이란 두 번째 트랜잭션이 "트랜잭션 재시작 시도"라는 오류 메시지와 함께 시간 초과됨을 의미했습니다. 그러나 다시 시간이 초과되기 때문에 결과가 없을 것입니다. 따라서 두 번째 트랜잭션은 차단됩니다. 시간이 초과되어 완료되지 않습니다.
Trevor Burnham

@ 트레버 : 당신의 문구가 완벽하게 명확했다. 당신은 단순히 완전히 다른 것을 요청하기 위해 단순히 질문을 업데이트했습니다.
hobodave

문제는 항상 같습니다. 첫 번째 트랜잭션이 커밋되거나 롤백되지 않을 때 두 번째 트랜잭션이 무기한으로 차단되는 것은 문제 입니다. 완료 ROLLBACK하는 데 n몇 초 이상이 걸리면 첫 번째 트랜잭션 을 강제로 실행하고 싶습니다 . 그렇게 할 방법이 있습니까?
Trevor Burnham

1
@TrevorBurnham 또한 MYSQL이 시나리오를 방지하기 위해 구성이없는 이유 가 궁금 합니다. 클라이언트의 무책임으로 인해 서버 중단이 허용되지 않기 때문입니다. 귀하의 질문을 이해하는 데 어려움이 없었으며 또한 관련이 있습니다.
Dinoop paloli

답변:



3

여기에 ServerFault에 대한 질문이 있기 때문에 특히 시스템 관리자 및 / 또는 DBA가 전문 지식을 보유하고 있다는 지식 영역에서 MySQL 문제에 대한 MySQL 솔루션을 찾고 있다고 가정하는 것이 합리적입니다. 섹션은 귀하의 질문을 다룹니다 :

첫 번째 트랜잭션이 커밋되거나 롤백되지 않으면 두 번째 트랜잭션이 무기한 차단됩니다.

아뇨. 당신이 이해하지 못하는 것 같아요 innodb_lock_wait_timeout. 정확히 필요한 것을 수행합니다.

매뉴얼에 명시된 오류와 함께 반환됩니다.

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  • 정의상 이것은 무기한 이 아닙니다 . 응용 프로그램이 다시 연결되어 반복적으로 차단되는 경우 응용 프로그램 은 트랜잭션이 아니라 "무한하게 차단"된 것입니다. 두 번째 트랜잭션은 innodb_lock_wait_timeout몇 초 동안 매우 확실하게 차단 됩니다.

기본적으로 트랜잭션은 롤백되지 않습니다. 이 오류 처리 방법, 다시 시도하는지 또는 롤백 여부를 결정하는 것은 응용 프로그램 코드의 책임입니다.

자동 롤백을 원하면 매뉴얼에 설명되어 있습니다.

현재 트랜잭션이 롤백되지 않습니다. 전체 트랜잭션을 롤백하려면 --innodb_rollback_on_timeout옵션으로 서버를 시작하십시오 .


RE : 수많은 업데이트 및 의견

먼저, 귀하는 귀하의 의견에서 귀하 가 무기한으로 차단 되는 첫 번째 거래 를 시간 초과하는 방법을 원한다고 말하고 있습니다 . 이는 원래 질문에서 명확하지 않으며 "첫 번째 트랜잭션이 커밋되거나 롤백되지 않으면 두 번째 트랜잭션이 무기한 차단됩니다"와 충돌합니다.

그럼에도 불구하고, 나는 그 질문에도 대답 할 수 있습니다. MySQL 프로토콜에는 "쿼리 타임 아웃"이 없습니다. 즉, 첫 번째 차단 된 트랜잭션을 시간 초과 할 수 없습니다. 완료 될 때까지 기다리거나 세션을 종료해야합니다. 세션이 종료되면 서버는 자동으로 트랜잭션을 롤백합니다.

다른 대안은 비 차단 I / O를 사용하는 mysql 라이브러리를 사용하거나 작성하는 것입니다.이 라이브러리는 N 초 후에 응용 프로그램 이 스레드 / 포크를 쿼리하여 종료시킬 수 있습니다. 이러한 라이브러리의 구현 및 사용은 ServerFault의 범위를 벗어납니다. 이것은 StackOverflow에 대한 적절한 질문입니다 .

둘째, 귀하의 의견에 다음을 언급했습니다.

실제로 MySQL의 끝에서 트랜잭션이 오래 걸리는 트랜잭션보다 트랜잭션 과정에서 클라이언트 앱이 멈추는 (즉 무한 루프에 걸리는) 시나리오에 대한 내 질문에 더 관심이있었습니다.

이것은 원래 질문에서 전혀 분명하지 않았으며 여전히 그렇지 않습니다. 의견에서이 중요한 내용을 공유 한 후에 만이를 식별 할 수 있습니다.

이것이 실제로 해결하려는 문제라면 잘못된 포럼에서 질문 한 것 같습니다. MySQL이 제공 할 수없고이 커뮤니티의 범위를 벗어난 프로그래밍 솔루션이 필요한 애플리케이션 레벨 프로그래밍 문제에 대해 설명했습니다. 최신 답변은 "Ruby 프로그램이 무한 반복되는 것을 어떻게 방지합니까?"라는 질문에 대한 답변입니다. 이 질문은이 커뮤니티에서 주제가 아니므로 StackOverflow에 문의해야합니다 .


죄송하지만 트랜잭션이 잠금을 기다리는 동안 (제안서의 이름 및 문서로 innodb_lock_wait_timeout) 사실은 아니지만 상황은 그렇지 않습니다. 클라이언트가 변경을 수행하기 전에 중단되거나 충돌하는 COMMIT경우 ROLLBACK.
Trevor Burnham

@ 트레버 : 당신은 단순히 잘못입니다. 이것은 끝까지 테스트하기가 쉽지 않습니다. 방금 테스트를 마쳤습니다. downvote를 다시 가져 오십시오.
hobodave

@hobo, 당신의 권리가 그가 그것을 좋아한다는 것을 의미하지 않기 때문에.
Chris S

크리스, 어떻게 옳은지 설명해 줄래? 첫 번째 트랜잭션을 해결하기 위해 메시지가 COMMIT없거나 ROLLBACK전송 되지 않으면 두 번째 트랜잭션은 어떻게 발생 합니까? Hobodave, 테스트 코드를 제시하면 도움이 될 것입니다.
Trevor Burnham

2
@ 트레버 : 당신은 조금 이해가되지 않습니다. 거래를 직렬화하고 싶습니까? 예, 당신은 다른 질문에서 그렇게 말했고이 질문에서 이것을 반복합니다. 첫 번째 거래가 완료되지 않은 경우 두 번째 거래가 어떻게 완료 될 것으로 예상하십니까? 해당 행에서 잠금이 해제 될 때까지 기다리라고 명시 적으로 지시했습니다. 따라서 기다려야합니다. 이것에 대해 무엇을 이해하지 못합니까?
hobodave

1

비슷한 상황에서 우리 팀은 pt-kill ( https://www.percona.com/doc/percona-toolkit/2.1/pt-kill.html ) 을 사용하기로 결정했습니다 . 너무 오래 실행되는 쿼리를보고하거나 종료 할 수 있습니다. 그런 다음 X 분마다 cron에서 실행할 수 있습니다.

문제는 예기치 않게 긴 (예를 들어, 무한한) 실행 시간을 가진 쿼리가 읽기 잠금으로 테이블을 플러시하려고 시도한 백업 프로 시저를 잠그고 "테이블 플러시 대기"에 대한 다른 모든 쿼리를 잠그고 시간이 초과되지 않는 문제였습니다. 잠금을 기다리지 않기 때문에 응용 프로그램에 오류가 발생하지 않았으며 결국 관리자와 응용 프로그램 중지에 대한 경고가 표시되지 않았습니다.

이 수정은 분명히 초기 검색어를 수정하는 것이었지만 향후 실수로 발생하는 상황을 피하기 위해 특정 시간보다 오래 지속되는 트랜잭션 / 쿼리를 자동 종료 (또는보고) 할 수 있다면 좋을 것입니다. 질문의 저자가 묻는 것 같습니다. 이것은 pt-kill로 가능합니다.


0

간단한 해결책은 필요한 timeout시간 보다 많은 시간이 걸리는 쿼리를 종료하고 innodb_rollback_on_timeout옵션과 함께 사용 하는 저장 프로 시저를 보유하는 것입니다.


-2

인터넷 검색을 한동안 수행 한 후 거래 당 시간 제한을 직접 설정할 수있는 방법이 없는 것 같습니다 . 내가 찾은 최고의 솔루션은 다음과 같습니다.

명령

SHOW PROCESSLIST;

현재 실행중인 모든 명령과 시작된 시간 (초)이 포함 된 테이블을 제공합니다. 클라이언트가 명령 제공을 중지하면 Sleep명령 을 제공하는 것으로 설명되며 Time마지막 명령이 완료된 이후 의 열 수는 초입니다. 따라서 데이터베이스에 5 초 이상 걸리지 않는 쿼리가 있다고 확신 할 경우 수동으로 들어가서 5 초가 넘는 KILL모든 Time값 을 가질 수 있습니다 ( 예 : 5 초). 예를 들어 id가 3 인 프로세스의 Time열 값이 12 인 경우 다음을 수행 할 수 있습니다.

KILL 3;

KILL 구문에 대한 문서는 해당 ID를 가진 스레드가 수행하는 트랜잭션이 롤백 될 것을 제안합니다 (테스트하지 않았으므로 조심하십시오).

그러나 이것을 자동화하고 5 초마다 모든 초과 근무 스크립트를 죽이는 방법은 무엇입니까? 같은 페이지 의 첫 번째 주석은 힌트를 제공하지만 코드는 PHP로되어 있습니다. 우리가 할 수없는 것 같습니다 SELECTSHOW PROCESSLISTMySQL 클라이언트 내에서. 여전히 매초마다 실행되는 PHP 데몬이 있다고 가정 $MAX_TIME하면 다음과 같이 보일 수 있습니다.

$result = mysql_query("SHOW FULL PROCESSLIST");
while ($row=mysql_fetch_array($result)) {
  $process_id=$row["Id"];
    if ($row["Time"] > $MAX_TIME) {
      $sql="KILL $process_id";
      mysql_query($sql);
  }
}

이는 유휴 클라이언트 연결뿐만 아니라 모든 유휴 클라이언트 연결을 강제 로 다시 연결 하는 부작용이 있습니다 .

더 나은 답변을 가진 사람이 있으면 듣고 싶습니다.

편집 :KILL 이 방법 으로 사용할 위험이 하나 이상 있습니다 . 위의 스크립트 KILL를 10 초 동안 유휴 상태 인 모든 연결에 사용한다고 가정하십시오 . 연결이 10 초 동안 유휴 상태이지만 연결이 해제되기 전에 KILL연결이 끊어진 사용자가 START TRANSACTION명령을 실행합니다. 그런 다음 KILL에드입니다. 그들은를 보낸 UPDATE다음 두 번 생각하고를 발행합니다 ROLLBACK. 그러나 KILL원래 트랜잭션을 롤백 했으므로 업데이트가 즉시 진행되어 롤백 할 수 없습니다! 테스트 사례는 이 게시물 을 참조하십시오 .


2
이 의견은이 답변을 읽는 독자를위한 것입니다. 수락 된 답변이라도이 작업을 수행하지 마십시오.
hobodave

좋아요, 부정적인 의견을 내리고 떠나는 것이 아니라 왜 이것이 나쁜 생각인지 설명하는 것은 어떻습니까? 원래 PHP 스크립트를 게시 한 사람은 서버가 정지되는 것을 막았다 고 말했다. 같은 목표를 달성 할 수있는 더 좋은 방법이 있다면 보여주세요.
Trevor Burnham

6
이 의견은 속죄되지 않습니다. 장래의 모든 독자가 "솔루션"을 사용하려고 시도하지 않도록 경고합니다. 장기 실행 트랜잭션을 자동으로 종료하는 것은 일방적으로 끔찍한 아이디어입니다. 절대 해서는 안됩니다 . 이것이 설명입니다. 킬 세션은 예외가 아니라 표준이어야합니다. 문제 의 근본 원인 은 사용자 오류입니다. 행을 잠근 다음 "지워"있는 DBA에 대한 기술 솔루션을 작성하지 마십시오. 당신은 그들을 해고합니다.
hobodave

-2

hobodave가 의견에서 제안한 것처럼 일부 클라이언트 ( mysql명령 행 유틸리티 는 아니지만 )에서 트랜잭션의 시간 제한을 설정할 수 있습니다. Ruby에서 ActiveRecord를 사용하는 데모는 다음과 같습니다.

require 'rubygems'
require 'timeout'
require 'active_record'

Timeout::timeout(5) {
  Foo.transaction do
    Foo.create(:name => 'Bar')
    sleep 10
  end
}

이 예 에서 트랜잭션은 5 초 후에 시간 초과되고 자동으로 롤백됩니다 . ( hobodave의 의견에 대한 응답으로 업데이트 : 데이터베이스가 응답하는 데 5 초 이상이 걸리는 경우 트랜잭션은 빨리 롤백됩니다.) 모든 트랜잭션이 n몇 초 후에 시간 초과되도록하려면 , ActiveRecord 주위에 래퍼를 만들 수 있습니다. 나는 이것이 Java, .NET, Python 등에서 가장 인기있는 라이브러리에도 적용된다고 생각하지만 아직 테스트하지는 않았습니다. (있는 경우이 답변에 의견을 게시하십시오.)

ActiveRecord의 KILL트랜잭션은 명령 줄에서 수행 된 트랜잭션과 달리 a 가 발급 된 경우 안전하다는 이점이 있습니다 . /dba/1561/mysql-client-believes-theyre-in-a-transaction-gets-killed-wreaks-havoc를 참조 하십시오 .

다른 답변에 게시 한 것과 같은 스크립트를 제외하고는 서버 측에서 최대 트랜잭션 시간을 적용하는 것이 불가능한 것으로 보입니다.


ActiveRecord는 동기식 (블로킹) I / O를 사용한다고 간과했습니다. 스크립트가 수행하는 모든 작업은 Ruby sleep 명령을 중단하는 것입니다. Foo.create명령이 5 분 동안 차단 된 경우 제한 시간이 종료되지 않습니다. 이것은 귀하의 질문에 제공된 예와 호환되지 않습니다. A SELECT ... FOR UPDATE는 다른 쿼리와 마찬가지로 오랫동안 쉽게 차단할 수 있습니다.
hobodave

거의 모든 mysql 클라이언트 라이브러리는 블로킹 I / O를 사용하므로 비 대답 I / O를 사용하는 자신의 것을 사용하거나 작성해야한다는 제 제안의 제안입니다. 다음은 쓸모없는 타임 아웃의 간단한 데모입니다. gist.github.com/856100
hobodave

실제로 MySQL의 끝에서 트랜잭션이 오래 걸리는 트랜잭션보다 트랜잭션 과정에서 클라이언트 앱이 멈추는 (즉 무한 루프에 걸리는) 시나리오에 대한 내 질문에 더 관심이있었습니다. 하지만 고맙습니다. 좋은 지적입니다. 참고로 질문을 업데이트했습니다.)
Trevor Burnham

소유권이 주장 된 결과를 복제 할 수 없습니다. gist.github.com/856100 을 활용하기 위해 요점을 업데이트했습니다 ActiveRecord::Base#find_by_sql. 다시 말하지만 AR은 차단 I / O를 사용하기 때문입니다.
hobodave

@hobodave 예, 해당 편집 내용이 잘못되어 수정되었습니다. 명확하게 :이 timeout방법은 트랜잭션을 롤백하지만 MySQL 데이터베이스가 쿼리에 응답 할 때까지는 아닙니다. 따라서이 timeout방법은 클라이언트 측의 긴 트랜잭션이나 비교적 짧은 쿼리 몇 개로 구성된 긴 트랜잭션을 방지하는 데 적합하지만 단일 쿼리가 범인 인 긴 트랜잭션에는 적합하지 않습니다.
Trevor Burnham
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.