온프레미스나 EC2 기반 IaaS 환경에서 MySQL을 운영하던 DBA 및 데이터 아키텍트가 Azure Database for MySQL(Flexible Server/Single Server)과 같은 완전관리형(PaaS) 환경으로 넘어올 때 가장 먼저 맞닥뜨리는 장벽 중 하나는 제한된 관리자 권한(Restricted Superuser Privilege)입니다.
대표적인 사례가 롱런 트랜잭션(Long-running Transaction)이나 락 경합(Lock Contention)으로 인해 특정 세션을 긴급 종료해야 할 때 발생하는 ERROR 1095 (HY000): You are not owner of thread 또는 Access denied; you need (at least one of) the SYSTEM_USER privilege(s) 오류입니다.
Azure PaaS MySQL에서는 표준 DDL/DCL 명령 대신 전용 관리 저장 프로시저(mysql.az_*)를 통해 플랫폼 통제권과 사용자 제어권의 균형을 유지합니다. 이번 포스팅에서는 az_kill을 비롯한 Azure PaaS 전용 루틴의 내부 메커니즘과, 이를 활용한 락 세션 추적 및 강제 종료 실무 팁을 정리합니다.
1. 왜 PaaS 환경에서는 표준 KILL 명령어가 제한되는가?
온프레미스 vs PaaS의 권한 모델 차이
온프레미스 MySQL에서는 최고 관리자 계정에 SUPER 또는 CONNECTION_ADMIN + SYSTEM_USER 권한이 부여됩니다. 따라서 어떤 계정이 생성한 스레드이든 상관없이 KILL [CONNECTION | QUERY] <thread_id> 구문으로 즉각 종료할 수 있습니다.
그러나 Azure Flexible Server를 프로비저닝할 때 생성하는 마스터 계정은 진정한 의미의 root가 아닙니다. Azure 플랫폼 레벨의 백그라운드 워커(고가용성 헬스체크, 복제 모니터링, 자동 백업 에이전트 등)를 보호하기 위해, 사용자 계정에는 다음과 같은 제약이 적용됩니다.
- SUPER 및 SYSTEM_USER 권한 미부여: MySQL 8.0의 세분화된 동적 권한 모델에서도 사용자에게 플랫폼 시스템 스레드를 죽일 수 있는 권한을 주지 않습니다.
- 타 계정 스레드 킬 불가: 기본 KILL 문은 자신의 세션에서 생성한 스레드만 종료할 수 있으며, 타 세션의 스레드를 대상으로 실행하면 권한 오류가 발생합니다.
이 문제를 해결하기 위해 Azure는 mysql 시스템 데이터베이스 내에 SECURITY DEFINER로 선언된 전용 저장 프로시저를 제공합니다.
2. mysql.az_kill 및 PaaS 관리 프로시저 심층 분석
Azure는 플랫폼 데몬을 건드리지 않으면서 일반 테넌트 스레드만 안전하게 필터링하여 종료할 수 있도록 래핑된 루틴을 제공합니다.
A. mysql.az_kill(thread_id)의 내부 메커니즘
Azure 환경에서 특정 쿼리 또는 커넥션을 중단하려면 다음 프로시저를 호출합니다.
CALL mysql.az_kill(thread_id);
이 프로시저는 내부적으로 다음과 같은 단계를 거쳐 실행됩니다.
- 타깃 검증: 입력받은 thread_id가 Azure 인프라 유지보수용 시스템 계정(예: azure_superuser, replication_user 등)인지 검증합니다.
- 권한 상승 실행 (SECURITY DEFINER): 프로시저 내부의 소유자(최고 권한 계정) 컨텍스트로 전환하여 실제 KILL CONNECTION <thread_id> 명령을 실행합니다.
- 결과 리턴: 시스템 스레드인 경우 실행을 거부하여 클러스터 무결성을 방어하고, 사용자 스레드일 경우에만 시그널을 전달해 롤백(Rollback) 및 세션 정리를 유도합니다.
B. 함께 알아두어야 할 핵심 mysql.az_* 루틴
Azure PaaS에서는 OS 터미널이나 파일 시스템 접근이 불가능하므로, 통상 OS 레벨에서 처리하던 작업도 프로시저로 추상화되어 있습니다.
| 프로시저 명 | 용도 및 설명 | 온프레미스 대체 명령 |
| mysql.az_kill(thread_id) | 사용자 커넥션 및 해당 트랜잭션 강제 종료 | KILL <thread_id> |
| mysql.az_kill_query(thread_id) | 커넥션은 유지한 채 현재 실행 중인 쿼리만 취소 (일부 버전/엔진 지원) | KILL QUERY <thread_id> |
| mysql.az_load_timezone() | IANA 타임존 테이블 최신화 (mysql.time_zone* 갱신) | mysql_tzinfo_to_sql 유틸리티 |
실무 팁 (az_load_timezone): CONVERT_TZ() 함수 사용 시 NULL이 반환된다면 OS 타임존 데이터가 로드되지 않은 상태입니다. 온프레미스처럼 쉘에서 mysql_tzinfo_to_sql을 실행할 수 없으므로, 반드시 CALL mysql.az_load_timezone();을 한 번 실행해 주어야 합니다.
3. 실무 트러블슈팅: 락 블로킹 세션 추적 및 az_kill 대응 흐름
대량 마이그레이션(Airflow, Data Factory 등)이나 장기 트랜잭션으로 인해 애플리케이션 행(Hang) 상태가 발생했을 때, 메타데이터 락(MDL) 또는 InnoDB 락 경합을 추적하고 az_kill로 해소하는 표준 절차입니다.
Step 1. 블로킹 세션 식별 (sys 스키마 및 Performance Schema)
가장 직관적인 방법은 MySQL의 sys 스키마 뷰를 활용해 Lock Wait을 유발하는 Root Holder를 찾는 것입니다.
-- 1) InnoDB 락 경합 상태 및 블로킹 스레드 즉시 확인
SELECT
waiting_trx_id,
waiting_pid,
waiting_query,
blocking_trx_id,
blocking_pid,
blocking_query,
wait_age,
sql_kill_blocking_connection
FROM sys.innodb_lock_waits;
출력 컬럼 중 sql_kill_blocking_connection은 표준 문법(KILL <pid>)을 생성하므로, Azure에서는 해당 blocking_pid를 추출해 az_kill 파라미터로 넘겨야 합니다.
만약 DDL 작업으로 인해 Metadata Lock (MDL)이 발생하여 후속 DML/SELECT 쿼리들이 Waiting for table metadata lock 상태로 줄을 서고 있다면 다음 쿼리로 추적합니다.
-- 2) 메타데이터 락(MDL)을 잡고 있는 세션 추적
SELECT
ps.PROCESSLIST_ID AS blocking_thread_id,
ps.PROCESSLIST_USER,
ps.PROCESSLIST_HOST,
ps.PROCESSLIST_DB,
ps.PROCESSLIST_INFO AS current_statement,
ml.OBJECT_TYPE,
ml.OBJECT_SCHEMA,
ml.OBJECT_NAME,
ml.LOCK_TYPE,
ml.LOCK_DURATION
FROM performance_schema.metadata_locks ml
JOIN performance_schema.threads th ON ml.OWNER_THREAD_ID = th.THREAD_ID
JOIN performance_schema.processlist ps ON th.PROCESSLIST_ID = ps.PROCESSLIST_ID
WHERE ml.LOCK_STATUS = 'GRANTED'
AND ps.PROCESSLIST_ID != CONNECTION_ID()
ORDER BY ml.OBJECT_NAME;
Step 2. az_kill 실행 및 롤백 모니터링
블로킹 스레드 ID가 1245로 확인되었다면 프로시저를 호출합니다.
-- 블로킹 원인 세션 종료
CALL mysql.az_kill(1245);
주의: 대용량 트랜잭션 롤백 시 주의사항
az_kill은 트랜잭션을 "즉시 증발"시키는 마법이 아닙니다. 대상 스레드에 인터럽트 시그널을 전달하여 InnoDB Undo Log를 순회하며 롤백(Rollback)을 수행하도록 강제합니다.
- 대량 INSERT, UPDATE, DELETE가 진행 중이던 세션을 죽이면, 롤백 작업으로 인해 디스크 I/O(Redo/Undo 기록)와 CPU 부하가 역으로 급증할 수 있습니다.
- 롤백 진행 상황은 information_schema.innodb_trx 테이블의 trx_rows_modified 수치가 점진적으로 줄어드는지 확인하여 모니터링해야 합니다.
SELECT
trx_id,
trx_mysql_thread_id,
trx_state,
trx_started,
trx_rows_locked,
trx_rows_modified
FROM information_schema.innodb_trx
WHERE trx_mysql_thread_id = 1245;
4. 권장 아키텍처 및 예방책
- 타임아웃 파라미터 사전 튜닝:(해당 옵션이 장애의 원인이 될 수도 있으므로 신중하게 수치 지정)
- 트랜잭션이 영구적으로 락을 잡고 대기하지 않도록 인스턴스 파라미터(Server Parameters)에서 기본값을 조정합니다.
- innodb_lock_wait_timeout: 기본 50초에서 애플리케이션 특성에 맞게 단축 (온라인 서비스 기준 5~15초 권장).
- lock_wait_timeout (MDL 타임아웃): 기본 31536000초(1년)는 위험하므로 수십~수백 초 단위로 설정하여 DDL 대기로 인한 전체 서비스 장애 방지.
- 트랜잭션이 영구적으로 락을 잡고 대기하지 않도록 인스턴스 파라미터(Server Parameters)에서 기본값을 조정합니다.
- 배치/마이그레이션 커밋 단위 분할:
- 대량 이관 작업 시 단일 트랜잭션으로 밀어 넣지 않고, Chunk 단위(예: 1,000~5,000건)로 COMMIT을 발생시켜 Undo Log 비대화 및 az_kill 시의 롤백 병목을 방지합니다.
- 읽기 작업 분리:
- 장시간 수행되는 분석 리포팅 쿼리는 프라이머리 노드가 아닌 Flexible Server의 Read Replica(읽기 전용 복제본)로 라우팅하여 프라이머리 노드의 트랜잭션 잠금 리스크를 원천 차단합니다.
