2020년 10월 17일 토요일

XtraBackup에 의해서 발생하는 락 경합에 대하여

MySQL 백업을 할 때 사용하는 XtraBackup tool의 락 경합에 의해 일어날 수 있는 이슈에 대해 알아보자.
Single 구성 뿐만 아니라 Master-Slave에서도 일어날 수 있다

테스트 한 환경은 MySQL 5.7과 XtraBackup 2.4이다

XtraBackup 스크립트 작성
간단하게 백업을 실행하는 스크립트를 아래와 같이 작성 후 실행

#!/bin/bash
# test.sh

xtrabackup --backup --datadir=/var/lib/mysql/ --target-dir=/data/backups/mysql/

실행 로그
201016 14:00:11 >> log scanned up to (4633666800)
201016 14:00:11 [01]        ...done
201016 14:00:11 [01] Copying ./test/pg_category_mst.ibd to /data/backups/mysql/wtpayweb/test/pg_category_mst.ibd
201016 14:00:11 [01]        ...done
201016 14:00:11 [01] Copying ./test/wm_log.ibd to /data/backups/mysql/test/wm_log.ibd
201016 14:00:12 [01]        ...done
...skipping...
201016 14:00:13 Executing FLUSH NO_WRITE_TO_BINLOG TABLES...
201016 14:00:13 Executing FLUSH TABLES WITH READ LOCK...
맨 마지막 두 줄 
FLUSH NO_WRITE_TO_BINLOG TABLES, FLUSH TABLES WITH READ LOCK이 락 경합을 일으킬 수 있는 원인이 된다.

락 경합에 의해 발생하는 이슈
1. Replication lag
    - FLUSH TABLES 명령어는 meta data lock을 요구하므로 앞단의 쿼리가 끝날 때 까지 대기한다
# Master에서 테이블 생성
mysql> SELECT COUNT(1) SlaveThreadCount FROM information_schema.processlist WHERE user='system user';
+------------------+
| SlaveThreadCount |
+------------------+
|                0 |
+------------------+
1 row in set (0.01 sec)

mysql> CREATE TABLE lock_test (
    -> id int not null auto_increment,
    -> title varchar(50),
    -> primary key(id)
    -> ) engine=innodb;
Query OK, 0 rows affected (0.05 sec)

mysql> INSERT INTO lock_test (title) VALUES ('First title');
Query OK, 1 row affected (0.02 sec)

# Slave에서 20초 걸리는 SELECT실행
connection-1> SELECT *, SLEEP(20) FROM lock_test;

# 백업을 진행하고 있는 프로세스에서 아래의 명령어가 실행되었다고 가정
connection-2> FLUSH NO_WRITE_TO_BINLOG TABLES;

# Master에서 Insert
mysql> INSERT INTO lock_test (title) VALUES ('Second title');
Query OK, 1 row affected (0.01 sec)

# Slave processlist
connection-3> SHOW PROCESSLIST;
+----+-------------+-----------+------------+---------+-------+----------------------------------+-------------------------------------------------------+
| Id | User        | Host      | db         | Command | Time  | State                            | Info                                                  |
+----+-------------+-----------+------------+---------+-------+----------------------------------+-------------------------------------------------------+
| 20 | system user |           | NULL       | Connect | 80358 | Waiting for master to send event | NULL                                                  |
| 22 | system user |           | test | Connect |     5 | Waiting for table flush          | INSERT INTO lock_test (title) VALUES ('Second title') |
| 28 | root        | localhost | test | Query   |    12 | User sleep                       | SELECT *, SLEEP(20) FROM lock_test                    |
| 29 | root        | localhost | NULL       | Query   |    11 | Waiting for table flush          | FLUSH NO_WRITE_TO_BINLOG TABLES                       |
| 30 | root        | localhost | test | Query   |     0 | init                             | SHOW PROCESSLIST                                      |
+----+-------------+-----------+------------+---------+-------+----------------------------------+-------------------------------------------------------+
5 rows in set (0.00 sec)

connection-3> SHOW PROCESSLIST;
+----+-------------+-----------+------------+---------+-------+----------------------------------+-------------------------------------------------------+
| Id | User        | Host      | db         | Command | Time  | State                            | Info                                                  |
+----+-------------+-----------+------------+---------+-------+----------------------------------+-------------------------------------------------------+
| 20 | system user |           | NULL       | Connect | 80363 | Waiting for master to send event | NULL                                                  |
| 22 | system user |           | test | Connect |    10 | Waiting for table flush          | INSERT INTO lock_test (title) VALUES ('Second title') |
| 28 | root        | localhost | test | Query   |    17 | User sleep                       | SELECT *, SLEEP(20) FROM lock_test                    |
| 29 | root        | localhost | NULL       | Query   |    16 | Waiting for table flush          | FLUSH NO_WRITE_TO_BINLOG TABLES                       |
| 30 | root        | localhost | test | Query   |     0 | init                             | SHOW PROCESSLIST                                      |
+----+-------------+-----------+------------+---------+-------+----------------------------------+-------------------------------------------------------+
5 rows in set (0.00 sec)

connection-3> SHOW PROCESSLIST;
+----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------+
| Id | User        | Host      | db         | Command | Time  | State                                                                       | Info             |
+----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------+
| 20 | system user |           | NULL       | Connect | 80403 | Waiting for master to send event                                            | NULL             |
| 22 | system user |           | NULL       | Connect |    50 | Slave has read all relay log; waiting for the slave I/O thread to update it | NULL             |
| 28 | root        | localhost | test | Sleep   |    57 |                                                                             | NULL             |
| 29 | root        | localhost | NULL       | Sleep   |    56 |                                                                             | NULL             |
| 30 | root        | localhost | test | Query   |     0 | init                                                                        | SHOW PROCESSLIST |
+----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------+
5 rows in set (0.00 sec)

2. 접속 지연
    - 1번과 같은 상황에서 DB 접속을 하기 위해선 "-A" 옵션 지정해야 한다. 미지정 시 대기상태에 빠진다

[root@346ecc2d8c40 /]# mysql -uroot -p test_db_01
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
...

3. Select waiting
    - 1번과 같은 상황에서 동일한 테이블에 대한 SELECT도 대기상태에 빠진다.

connection-4> SELECT * FROM my_table LIMIT 1;
+------+
| id   |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

connection-4> SELECT * FROM lock_test LIMIT 1;

connection-5> SHOW PROCESSLIST;
+----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------------------------+
| Id | User        | Host      | db         | Command | Time  | State                                                                       | Info                               |
+----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------------------------+
| 20 | system user |           | NULL       | Connect | 83315 | Waiting for master to send event                                            | NULL                               |
| 22 | system user |           | NULL       | Connect |  2962 | Slave has read all relay log; waiting for the slave I/O thread to update it | NULL                               |
| 28 | root        | localhost | test | Query   |    31 | User sleep                                                                  | SELECT *, SLEEP(20) FROM lock_test |
| 29 | root        | localhost | NULL       | Query   |    28 | Waiting for table flush                                                     | FLUSH NO_WRITE_TO_BINLOG TABLES    |
| 33 | root        | localhost | test | Query   |     0 | init                                                                        | SHOW PROCESSLIST                   |
| 37 | root | localhost | test | Query | 12 | Waiting for table flush | SELECT * FROM lock_test LIMIT 1 |
+----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------------------------+ 6 rows in set (0.01 sec) mysql> SHOW PROCESSLIST; +----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------------------------+ | 20 | system user | | NULL | Connect | 83323 | Waiting for master to send event | NULL | | 22 | system user | | NULL | Connect | 2970 | Slave has read all relay log; waiting for the slave I/O thread to update it | NULL | | 28 | root | localhost | test | Query | 39 | User sleep | SELECT *, SLEEP(20) FROM lock_test |
| 29 | root | localhost | NULL | Query | 36 | Waiting for table flush | FLUSH NO_WRITE_TO_BINLOG TABLES | | 33 | root | localhost | test | Query | 0 | init | SHOW PROCESSLIST |
| 37 | root | localhost | test | Query | 20 | Waiting for table flush | SELECT * FROM lock_test LIMIT 1 |
+----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------------------------+ 6 rows in set (0.00 sec) mysql> SHOW PROCESSLIST; +----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------+ | 20 | system user | | NULL | Connect | 83516 | Waiting for master to send event | NULL | | 22 | system user | | NULL | Connect | 3163 | Slave has read all relay log; waiting for the slave I/O thread to update it | NULL | | 28 | root | localhost | test | Sleep | 232 | | NULL |
| 29 | root | localhost | NULL | Sleep | 229 | | NULL | | 33 | root | localhost | test | Query | 0 | init | SHOW PROCESSLIST |
| 37 | root | localhost | test | Sleep | 213 | | NULL |
+----+-------------+-----------+------------+---------+-------+-----------------------------------------------------------------------------+------------------+ 6 rows in set (0.00 sec)

대책
1. xtrabackup 옵션인 "ftwrl-wait-timeout"을 설정하여 방지
2. MySQL 8.0부터 서포트된 "LOCK INSTANCE FOR BACKUP"을 사용

# Refer
https://www.percona.com/doc/percona-xtrabackup/2.4/xtrabackup_bin/xbk_option_reference.html
https://dev.mysql.com/doc/refman/5.7/en/flush.html
https://dev.mysql.com/doc/refman/8.0/en/lock-instance-for-backup.html

댓글 없음:

댓글 쓰기

결혼이민비자 신청방법(F-6-1 국민의 배우자)

 제가 일본인 여자친구와 결혼 후, 한국에 귀국하기 위해 신청한 결혼이민비자에 대하여 작성해보도록 하겠습니다. 필자는 일본에서 근무하고 있었으며, 한국에서의 소득은 없었습니다. 결혼이민비자를 신청한 날짜는 2021-04-21 이며, 사증이 발급된 날짜...