1.메타데이터 락
- 트랜잭션이 시작되면 사용하는 테이블에 대한 메타데이터 락을 걸고 트랜잭션이 완료될 때 메타데이터 락을 해제 한다. 이때 테이블의 정의를 바꾸려는 모든 스레드는 트랜잭션이 끝날때까지 기다려야 한다. (DDL, CREATE, DROP, ALTER)
2. 장애 상황
- session 1, session 2, session3의 3개의 세션 중에 session 1에서 트랜잭션을 시작(BEGIN) 명령어를 실행하고 SELECT문을 수행하였다. 같은 시간 session 2에서는 동일한 테이블을 drop 명령어를 수행했고, session 3에서는 데이터 insert 작업을 수행하였다.
- session 2, session 3는 metalock이 발생하였는데 lock을 해결 할 수 있는 가장 좋은 방법은 무엇인가?
3. 테스트
#테스트 환경
- CentOS 7
- MariaDB 10.3.8
3.1 메타데이터 락 발생
#session 1
MariaDB [test]> create table test.metalock ( id varchar(100));
Query OK, 0 rows affected (0.027 sec)
MariaDB [test]> insert into test.metalock(id) values('test1');
Query OK, 1 row affected (0.001 sec)
#트랜잭션 시작
MariaDB [test]> BEGIN;
Query OK, 0 rows affected (0.000 sec)
MariaDB [test]> select id from test.metalock;
+-------+
| id |
+-------+
| test1 |
+-------+
1 row in set (0.000 sec)
#session 2
# DDL metalock 발생
MariaDB [test]> drop table test.metalock;
#session 3
# DML metalock 발생(session 1이 아닌 session 2에 의해 metalock 발생)
,만약 session 2에서 DDL 작업이 없었다면 session 3은 metalock이 발생하지 않는다.
MariaDB [test]> insert into test.metalock(id) values('test2');
#session 4
# matalock에 의해 대기 중인 세션 확인
[hiwdbusr@hiwsvr01 ~]$ mysqladmin -uroot -pNetand1410 processlist |grep meta
| 379126 | root | localhost | | Query | 16 | Waiting for table metadata lock | drop table test.metalock | 0.000 |
| 379651 | root | localhost | test | Query | 4 | Waiting for table metadata lock | insert into test.metalock(id) values('test2') | 0.000 |
#session 4
# metalock 발생 세션 확인( 현재 쿼리가 수행 중이라면, TRX_QUERY 값에 쿼리가 출력되며, 쿼리 실행이 완료 되었다면 NULL로 표시 된다.)
MariaDB [information_schema]> SELECT A.TRX_ID
-> ,A.TRX_STATE
-> ,A.TRX_STARTED
-> ,A.TRX_QUERY
-> ,A.TRX_MYSQL_THREAD_ID
-> FROM INFORMATION_SCHEMA.INNODB_TRX A\G;
*************************** 1. row ***************************
TRX_ID: 422151421839768
TRX_STATE: RUNNING
TRX_STARTED: 2020-05-14 08:57:02
TRX_QUERY: NULL
TRX_MYSQL_THREAD_ID: 380511
1 row in set (0.000 sec)
#TRX_MYSQL_THREAD_ID 번호로 processlist에서 재확인
[hiwdbusr@hiwsvr01 ~]$ mysqladmin -uroot -pNetand1410 processlist |grep 380511
| 380511 | root | localhost | test | Sleep | 139 |
4. 해결 방법
해결방법 1 : meta lock을 유발시킨 세션을 kill 한다.
#session 4
[hiwdbusr@hiwsvr01 ~]$ mysqladmin -uroot -pNetand1410 kill 380511;
#session 2
# 트랜잭션 진행 중이던 session 1을 kill 함으로 session 2가 대기 현상을 끝내고 DML 작업 완료
MariaDB [(none)]> drop table test.metalock;
Query OK, 0 rows affected (4 min 21.864 sec)
#session 3
# session 2에 의해 metalock이 발생 했었고, session 2에서 테이블을 삭제 했기 때문에 테이블을 찾을 수 없다는 에러와 함께 작업 종료
MariaDB [test]> insert into test.metalock(id) values('test2');
ERROR 1146 (42S02): Table 'test.metalock' doesn't exist
해결방법 2 : meta lock에 걸린 세션을 kill 한다.
#session 4
# session 2,3을 모두 kill 한다.
[hiwdbusr@hiwsvr01 ~]$ mysqladmin -uroot -pNetand1410 kill 380511,379651;
해결방법 3 : lock_wait_timeout 파라미터 시간을 조정하여 lock 대기 시간을 줄인다.
( 단, 파라미터 변경 전에 접속 한 세션은 적용 대상이 아니며, 재접속 후에 반영 된다.)
#session 4
MariaDB [(none)]> show variables '%lock_wait_timeout%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| lock_wait_timeout | 86400 |
+--------------------------+-------+
MariaDB [(none)]> set global lock_wait_timeout = 2;
Query OK, 0 rows affected (0.000 sec)
#session 2
MariaDB [(none)]> drop table test.metalock;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
#session 3
MariaDB [test]> insert into test.metalock(id) values('test3');
Query OK, 1 row affected (0.002 sec)
5. 결론
- 트랜직션을 시작(BEGIN) 했다면, 반드시 COMMIT 또는 ROLLBACK을 명시해줘야 lock이 발생하지 않는다.
- metalock이 발생을 최소화 하기 위해선 파라미터(lock_wait_timeout)를 조절하여 트랜잭션이 진행 중일 때는 DDL작업 수행 시 바로 타임아웃 되게 한다.
- 운영 중에 이미 metalock이 발생 했다면, lock을 발생 시킨 세션을 끊어서 그 다음 작업을 진행되게 하거나 lock에 걸려 대기하고 있는 세션을 끊는 방법을 선택 한다.( 업무에 따라서 세션을 끊어도 서비스에 영향이 없는 방향으로 선택한다.)