mysqldump-단일 트랜잭션이지만 업데이트 쿼리가 백업을 기다리고 있습니다


10

문서에 따르면 mysqldump --single-transaction을 사용하면 읽기 잠금으로 테이블을 플러시하여 일관된 상태를 얻은 다음 트랜잭션을 시작해야하며 작가가 대기하지 않아야합니다.

그러나 나는 어젯밤에 다음과 같은 상황을 겪었습니다.

전체 프로세스 목록 표시에서 발췌 :

그중 수백 ...

   Command: Query
   Time: 291
   State: Waiting for table flush
   Info: insert into db_external_notification.....

그런 다음 :

Command: Query
Time: 1204
State: Sending data
Info: SELECT /*!40001 SQL_NO_CACHE */ * FROM `db_external_notification`

나머지 스레드는 잠자기 상태입니다

이 인서트가 무엇을 기다리고 있는지 아는 사람이 있습니까? FLUSH 테이블이나 DDL 또는 매뉴얼에 언급 된 쿼리가 대기하지 않는 것을 볼 수 없습니다.

전체 mysqldump 명령

mysqldump --quick --add-drop-table --single-transaction --master-data=2 -uxx -pxx dbname

나는 여기에 --quick가 중복되어 있다고 생각합니다. 아마도 이전부터 남은 것입니다.이 스크립트는 매우 오래되었지만 아무 것도 아프지 않아야합니다.


show full processlist 및 show innodb status (익명)의 전체 출력은 다음과 같습니다. pastebin.com/D7WS3QAE
Aleksandar Ivanisevic

전체 명령 줄은 mysqldump무엇입니까? 특히, --flush-logs또는 --master-data... 을 사용하고 있습니까? 옵션간에 잠재적 인 상호 작용이 있습니다.
Michael-sqlbot

보고 해 주셔서 감사합니다 전체 mysqldump 명령 추가
Aleksandar Ivanisevic

답변:


6

mysqldump--single-transaction 옵션은 하지 않습니다 . mysqldump는 덤프되는 모든 테이블에 대해 반복 가능한 읽기 트랜잭션을 설정합니다.FLUSH TABLES WITH READ LOCK;

귀하의 질문에 따르면, db_external_notification테이블에 대한 mysqldump의 SELECT 는 동일한 테이블에 수백 개의 INSERT 명령을 보유하고 있다고 언급했습니다 . 왜 이런 일이 발생합니까?

gen_clust_index 에 대한 잠금이 가장 가능성이 높습니다 (클러스터형 인덱스라고 함). 이 패러다임은 테이블의 데이터 및 인덱스 페이지가 공존하게합니다. 이러한 인덱스 페이지는 PRIMARY KEY 또는 자동 생성 된 RowID 인덱스 (PRIMARY KEY가없는 경우)를 기반으로합니다.

실행 SHOW ENGINE INNODB STATUS\G하여 이를 발견하고 독점 잠금이있는 gen_clust_index 에서 모든 페이지를 찾을 수 있어야합니다 . 클러스터형 인덱스가있는 테이블에 INSERT를 수행하려면 PRIMARY KEY의 BTREE를 처리하고 auto_increment의 직렬화를 처리하기위한 독점 잠금이 필요합니다.

나는이 현상을 전에 논의했다

업데이트 2014-07-21 15:03 EDT

PastBin의 614-617 행을보십시오

mysql tables in use 1, locked 0
MySQL thread id 6155315, OS thread handle 0x85f11b70, query id 367774810 localhost root Sending data
SELECT /*!40001 SQL_NO_CACHE */ * FROM `db_external_notification`
Trx read view will not see trx with id >= 1252538405, sees < 1252538391

617 행은

Trx read view will not see trx with id >= 1252538405, sees < 1252538391

이것이 무엇을 말해줍니까? auto_increment가 설정된 PRIMARY KEY가 있습니다 id.

id테이블 의 최대 값 은 mysqldump가 시작될 db_external_notification때보 1252538391다 적었습니다 . 1252538391에서 빼면 125253840514 개 이상의 INSERT 명령이 시도되었음을 나타냅니다. 내부적으로 이것은이 테이블의 auto_increment를 최소한 14 번 이동해야합니다. 그러나이 id차이 를 관리하기 때문에 커밋하거나 로그 버퍼로 푸시 할 수있는 것은 없습니다 .

이제 PasteBin의 프로세스 목록을보십시오. 잘못 계산하지 않으면 38 개의 DB 연결이 INSERT를 수행하는 것을 보았습니다 (19 mysqldump 프로세스 이전 (프로세스 id 6155315), 19 이후). auto_increment gap 관리로 인해 14 개 이상의 해당 연결이 고정되어 있다고 확신합니다.


나는 오랫동안 찾고 있었고 독점 잠금 장치를 찾을 수 없었습니다. pastebin.com/D7WS3QAE에 전체 innodb 상태를 붙여 넣었습니다. 나에게 독점 잠금처럼 보이는 것은 없습니다
Aleksandar Ivanisevic

설명 주셔서 감사합니다. 백업이 절대로 쓰지 않을 것이기 때문에 읽기 전용 트랜잭션을 사용하지 않는 이유가 궁금합니다. 그러나 엔터프라이즈 백업을 위해 해당 기능을 유지하고 있다고 생각합니다.
Aleksandar Ivanisevic

10

--single-transaction의 옵션을 사용 mysqldump 하지 DO가 FLUSH TABLES WITH READ LOCK이전에 백업 작업을 시작하기 특정 조건 하에서. 이러한 조건 중 하나는 --master-data옵션을 지정할 때 입니다.

mysql-5.6.19/client/mysqldump.c5797 행 의 소스 코드 :

if ((opt_lock_all_tables || opt_master_data ||
     (opt_single_transaction && flush_logs)) &&
    do_flush_tables_read_lock(mysql))
  goto err;

반복 가능한 읽기 트랜잭션을 시작하기 전에 정확한 binlog 좌표에 고정 잠금을 설정하려면 --master-data이 잠금을 획득 한 다음 binlog 좌표가 확보되면이 잠금이 트리거됩니다.

사실, mysqldump않는 FLUSH TABLESa로 이어 FLUSH TABLES WITH READ LOCK모두 일을하는 읽기 잠금이 초기 세척 시간이 좀 걸리는 경우 더 빨리 얻을 수 있기 때문이다.

...하나...

binlog 좌표를 얻 자마자 명령문을 mysqldump발행 UNLOCK TABLES하므로 시작한 플러시로 인해 차단되는 것이 없어야합니다. 보류 Waiting for table flush중인 트랜잭션의 결과로 스레드가 없어야합니다 mysqldump.

당신이에 스레드를 볼 때 Waiting for table flush상태, 즉 해야 것을 의미 FLUSH TABLES [WITH READ LOCK]문이 발행 된 여전히 실행중인 쿼리가 테이블 높이 기다려야합니다, 그래서 그것을 실행하기 전에 - 쿼리가 시작했을 때. 게시 한 프로세스 목록의 mysqldump경우이 동일한 테이블을 읽고 쿼리가 잠시 실행되었지만 차단 쿼리는 오랫동안 차단되지 않았습니다.

이것은 모두 다른 일이 일어 났음을 암시합니다.

내부적 으로 작동 하는 방식과 함께 버그 # 44884에 설명 된 오랜 문제가 FLUSH TABLES있습니다. 문제가 계속 지속되면 놀라지 않을 것입니다.이 문제가 해결이 매우 복잡한 문제이기 때문에이 문제가 "수정"된 경우 놀라 울 것입니다. 그것을 고치면 다른 것을 깨뜨 리거나 새롭고 다르며 여전히 바람직하지 않은 행동을 일으킬 위험이 있습니다.

이것이 당신이보고있는 것에 대한 설명 일 것 같습니다.

구체적으로 특별히:

  • 당신은 테이블 및 문제에 대해 실행중인 장기 실행 쿼리가있는 경우 FLUSH TABLES, 다음은 FLUSH TABLES장기 실행 쿼리 완료 될 때까지 차단합니다.

  • 또한 FLUSH TABLES발행 후 시작된 모든 쿼리 FLUSH TABLES는 완료 될 때까지 차단 됩니다.

  • 당신이 죽이면 추가, FLUSH TABLES쿼리를 차단하는 쿼리는 것이다 여전히 차단 , 원래 쿼리를 장기 실행에 차단 된 하나의 FLUSH TABLES살해에도 불구하고 있기 때문에, 쿼리를 FLUSH TABLES쿼리가 완료되지 않았습니다, 해당 테이블 (하나, 또는 더 많이, 장기 실행 쿼리 관련) 여전히 플러시되고 있으며 장기 실행 쿼리가 완료 되 자마자 보류중인 플러시가 발생하지만 이전은 아닙니다.

아마도 다른 프로세스, 아마도 다른 mysqldump, 잘못 된 쿼리 또는 잘못 작성된 모니터링 프로세스가 테이블을 플러시하려고 시도했을 것입니다.

이 쿼리는 알 수없는 메커니즘으로 인해 종료되거나 시간 초과되었지만 해당 mysqldump테이블에서 읽은 후에는 그 영향이 남아 있었습니다.

FLUSH TABLES장기 실행 쿼리가 처리되는 동안 시도하여이 조건을 복제 할 수 있습니다 . 그런 다음 다른 쿼리를 시작하면 차단됩니다. 그런 다음 FLUSH TABLES쿼리를 종료하면 최신 쿼리의 차단이 해제되지 않습니다. 그런 다음 첫 번째 쿼리를 종료하거나 완료하면 최종 쿼리가 성공적으로 실행됩니다.


나중에 생각할 때, 이것은 관련이 없습니다.

Trx read view will not see trx with id >= 1252538405, sees < 1252538391

mysqldump --single-transaction문제가 START TRANSACTION WITH CONSISTENT SNAPSHOT발생하여 덤프가 진행되는 동안 변경된 데이터를 덤프하지 못하도록 하기 때문에 정상 입니다. 그것이 없다면, 시작시 얻은 binlog 좌표는 의미가 없을 것이기 때문에 의미 --single-transaction가 없습니다. 이 Waiting for table flush트랜잭션은 분명히 잠금을 보유하지 않기 때문에 어떤 의미로도 문제 와 관련해서는 안됩니다 .


이 대답은 실제로 맞습니다.
Boban P.

2

: 나는 기능 요청 제출 https://support.oracle.com/epmos/faces/BugDisplay?id=27103902을 .

또한 5.6.37에 대한 패치를 작성하여 --single-transaction --slave-data와 --single-transaction --master-data 조합과 동일한 방법을 사용하며 보증없이 그대로 제공됩니다. 자신의 책임하에 사용하십시오.

--- mysql-5.6.37/client/mysqldump.c.bak 2017-11-14 12:24:41.846647514 -0600
+++ mysql-5.6.37/client/mysqldump.c 2017-11-14 14:17:51.187050091 -0600
@@ -4900,10 +4900,10 @@
   return 0;
 }

+/*
 static int do_stop_slave_sql(MYSQL *mysql_con)
 {
   MYSQL_RES *slave;
-  /* We need to check if the slave sql is running in the first place */
   if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS"))
     return(1);
   else
@@ -4911,23 +4911,21 @@
     MYSQL_ROW row= mysql_fetch_row(slave);
     if (row && row[11])
     {
-      /* if SLAVE SQL is not running, we don't stop it */
       if (!strcmp(row[11],"No"))
       {
         mysql_free_result(slave);
-        /* Silently assume that they don't have the slave running */
         return(0);
       }
     }
   }
   mysql_free_result(slave);

-  /* now, stop slave if running */
   if (mysql_query_with_error_report(mysql_con, 0, "STOP SLAVE SQL_THREAD"))
     return(1);

   return(0);
 }
+*/

 static int add_stop_slave(void)
 {
@@ -5841,10 +5839,12 @@
   if (!path)
     write_header(md_result_file, *argv);

+  /*
   if (opt_slave_data && do_stop_slave_sql(mysql))
     goto err;
+  */

-  if ((opt_lock_all_tables || opt_master_data ||
+  if ((opt_lock_all_tables || opt_master_data || opt_slave_data ||
        (opt_single_transaction && flush_logs)) &&
       do_flush_tables_read_lock(mysql))
     goto err;
@@ -5853,7 +5853,7 @@
     Flush logs before starting transaction since
     this causes implicit commit starting mysql-5.5.
   */
-  if (opt_lock_all_tables || opt_master_data ||
+  if (opt_lock_all_tables || opt_master_data || opt_slave_data ||
       (opt_single_transaction && flush_logs) ||
       opt_delete_master_logs)
   {
 static int add_stop_slave(void)
 {
@@ -5841,10 +5839,12 @@
   if (!path)
     write_header(md_result_file, *argv);

+  /*
   if (opt_slave_data && do_stop_slave_sql(mysql))
     goto err;
+  */

-  if ((opt_lock_all_tables || opt_master_data ||
+  if ((opt_lock_all_tables || opt_master_data || opt_slave_data ||
        (opt_single_transaction && flush_logs)) &&
       do_flush_tables_read_lock(mysql))
     goto err;
@@ -5853,7 +5853,7 @@
     Flush logs before starting transaction since
     this causes implicit commit starting mysql-5.5.
   */
-  if (opt_lock_all_tables || opt_master_data ||
+  if (opt_lock_all_tables || opt_master_data || opt_slave_data ||
       (opt_single_transaction && flush_logs) ||
       opt_delete_master_logs)
   {

FK 관계가있는 많은 InnoDB 테이블을 사용하여 매우 바쁜 마스터에 슬레이브로 다음 프로세스로 테스트했습니다.

  1. 슬레이브 A를 중지하십시오.
  2. ~ 15 분 정도 기다립니다.
  3. --single-transaction 및 --dump-slave = 2 옵션을 사용하여 슬레이브 B에서 DB 1을 덤프하십시오.
  4. 3 단계에서 덤프 좌표가 올 때까지 슬레이브 A를 시작하십시오.
  5. 슬레이브 A에서 DB 1과 2를 삭제하십시오.
  6. 슬레이브 A에서 빈 DB 1 및 2를 작성하십시오.
  7. 3 단계에서 덤프를 슬레이브 A로로드하십시오.
  8. 동일한 옵션으로 슬레이브 B에서 DB 2를 덤프하십시오. DB 2는 DB 1과 FK 관계가 있습니다.
  9. DB 2에 replicate_ignore_db를 추가하고 슬레이브 A에 skip_slave_start를 추가하십시오.
  10. 슬레이브 A를 다시 시작하십시오.
  11. 슬레이브 A에서 8 단계의 덤프 좌표가 될 때까지 슬레이브를 시작하십시오.
  12. 8 단계에서 덤프를 슬레이브 A로로드하십시오.
  13. 슬레이브 A에서 replicate_ignore_db 및 skip_slave_start 옵션을 제거하십시오.
  14. 슬레이브 A를 다시 시작하십시오.
  15. ~ 1 주간 기다리십시오.
  16. pt-checksum을 사용하여 데이터 무결성을 확인하십시오.

오라클의 패치 제출 프로세스는 다소 집중적이므로이 경로를 선택한 이유는 무엇입니까? Percona 및 / 또는 MariaDB를 사용하여 통합하려고 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.