---
title: "MySQL Error Log 해석: 시작, 비정상 종료, 복제, InnoDB 경고를 읽는 방법"
description: "MySQL Error Log의 구조와 startup, crash recovery, replication, InnoDB 경고 패턴을 장애 대응 관점에서 체계적으로 해석한다."
tags: [ MySQL, 운영, InnoDB, 복제, DBA ]
image: "mysql-report-bg.png"
published: "2026-08-29"
updated: "2026-08-29"
author: "MySQL 기술 노트"
source_url: ""
---

MySQL Error Log는 서버가 시작되지 않거나, 예고 없이 재시작되거나, 복제 지연이 갑자기 커졌을 때 가장 먼저 확인해야 하는 기록이다. 그러나 로그 한 줄만 떼어 보면 원인과 결과를 뒤집어 해석하기 쉽다. `ready for connections`는 정상 시작을 뜻하지만 그 앞에 수행된 crash recovery가 정상 종료 때문인지 전원 장애 때문인지는 알려 주지 않는다. InnoDB 경고 역시 즉시 장애를 의미할 수도 있고, 이미 복구된 일시적 현상의 흔적일 수도 있다.

이 장에서는 Error Log를 단순 문자열 모음이 아니라 **시간축, 심각도, error code, subsystem, 서버 생명주기**로 해석하는 방법을 정리한다. 기본 대상은 MySQL 8.0 이상이며, 파일 로그·systemd journal·컨테이너 표준 출력·`performance_schema.error_log`를 함께 다룬다.

## 1. Error Log가 기록하는 범위

Error Log에는 대체로 다음 사건이 기록된다.

- 서버 프로세스의 시작과 정상 종료
- 설정 파일 및 시스템 변수 검증 실패
- 플러그인·컴포넌트 초기화와 종료
- InnoDB redo 적용, undo rollback, tablespace 검사 등 recovery 과정
- 계정·TLS·인증 관련 경고
- Source/Replica 연결, relay log 적용, applier worker 오류
- 테이블 손상, I/O 오류, 메모리 부족, 내부 assertion과 비정상 종료
- 운영자가 실행한 shutdown 또는 외부 신호에 의한 종료

반대로 Error Log만으로 모든 SQL 장애를 설명할 수는 없다. 일반 쿼리 실패는 애플리케이션 응답이나 Performance Schema statement event에만 남을 수 있다. 느린 쿼리는 Slow Query Log, 접속 이력은 General Log 또는 감사 체계, 자원 고갈은 OS·컨테이너·클라우드 지표를 함께 확인해야 한다.

> Error Log는 서버 내부 관점의 사건 기록이다. 애플리케이션 오류 시각, OS 이벤트, 배포 이력, 연결 수와 I/O 지표를 같은 시간축에 놓아야 원인 분석이 완성된다.

## 2. 한 줄보다 생명주기 구간을 읽는다

MySQL 장애 분석에서 중요한 단위는 개별 메시지가 아니라 **시작부터 준비 완료까지**, 또는 **이상 징후부터 종료까지**의 연속 구간이다.

```mermaid
graph LR
    A[프로세스 시작] --> B[설정과 컴포넌트 초기화]
    B --> C[InnoDB 초기화]
    C --> D{정상 종료 흔적이 있는가}
    D -- 예 --> E[일반 기동]
    D -- 아니오 --> F[Crash recovery]
    F --> G[Redo 적용과 미완료 트랜잭션 정리]
    E --> H[네트워크 리스너 준비]
    G --> H
    H --> I[ready for connections]
    I --> J[정상 서비스]
    J --> K{종료 원인}
    K -- 관리자 요청 --> L[정상 shutdown]
    K -- OOM·신호·assertion --> M[비정상 종료]
```

기동 구간을 읽을 때는 다음 질문을 순서대로 적용한다.

1. **프로세스가 실제로 시작되었는가?** 설정 오류라면 InnoDB 초기화 전에 중단될 수 있다.
2. **이전 종료가 정상적이었는가?** 정상 shutdown 완료 메시지가 없고 다음 기동에서 recovery가 시작되면 비정상 종료 가능성이 높다.
3. **recovery가 어디까지 진행되었는가?** redo 적용, rollback, tablespace 검사 중 반복 중단되는지 확인한다.
4. **외부 접속을 받을 준비가 끝났는가?** `ready for connections` 이전에는 프로세스가 살아 있어도 서비스 준비가 완료된 것이 아니다.
5. **기동 후 즉시 종료하지 않았는가?** readiness 직후 fatal error, OOM, orchestrator restart가 이어지는 restart loop를 확인한다.

## 3. MySQL 8.0 Error Log 구조

MySQL 8.0의 Error Log 메시지는 다음 필드를 중심으로 해석할 수 있다.

| 필드 | 의미 | 운영 해석 |
|---|---|---|
| `LOGGED` 또는 timestamp | 사건 시각 | 서버·OS·애플리케이션 시각대가 같은지 먼저 확인한다. |
| `THREAD_ID` | 메시지를 남긴 내부 스레드 | 같은 작업 흐름의 메시지를 묶는 보조 키다. 재시작을 넘는 영구 식별자는 아니다. |
| `PRIO` | `System`, `Error`, `Warning`, `Note` | 심각도만으로 장애 여부를 단정하지 않는다. 후속 성공 메시지와 반복 빈도를 함께 본다. |
| `ERROR_CODE` | `MY-` 형식의 식별 코드 | 문구가 버전별로 달라도 같은 사건군을 검색하는 데 유리하다. |
| `SUBSYSTEM` | `Server`, `InnoDB`, `Repl` 등 | 담당 계층과 다음 조사 도구를 결정한다. |
| `DATA` | 사람이 읽는 메시지 | 객체명, 파일, LSN, 채널, 원격 endpoint 등 사건의 구체 정보를 담는다. |

텍스트 검색은 빠르지만 자동화 규칙은 가능하면 `ERROR_CODE + SUBSYSTEM + PRIO` 조합을 사용한다. 메시지 문구는 패치 버전에서 바뀔 수 있고, 언어·출력 sink·줄바꿈 방식에도 영향을 받기 때문이다. 다만 error code 하나가 항상 단일 원인을 뜻하는 것은 아니므로 최종 판단은 앞뒤 문맥으로 해야 한다.

### 3.1 현재 로그 설정과 조회 인터페이스 확인

다음 쿼리는 서버 버전, Error Log 관련 변수, `performance_schema.error_log` 제공 여부를 한 번에 확인한다.

```sql
SELECT VERSION() AS mysql_version;

SELECT VARIABLE_NAME, VARIABLE_VALUE
FROM performance_schema.global_variables
WHERE VARIABLE_NAME IN (
  'log_error',
  'log_error_services',
  'log_error_suppression_list',
  'log_error_verbosity'
)
ORDER BY VARIABLE_NAME;

SHOW TABLES FROM performance_schema LIKE 'error_log';
```

실행 결과(MySQL 8.0.x):

```text
mysql> SELECT VERSION() AS mysql_version;

+---------------+
| mysql_version |
+---------------+
| 8.0.46        |
+---------------+
1 row in set (0.00 sec)

mysql> SELECT VARIABLE_NAME, VARIABLE_VALUE
    -> FROM performance_schema.global_variables
    -> WHERE VARIABLE_NAME IN (
    ->   'log_error',
    ->   'log_error_services',
    ->   'log_error_suppression_list',
    ->   'log_error_verbosity'
    -> )
    -> ORDER BY VARIABLE_NAME;

+----------------------------+----------------------------------------+
| VARIABLE_NAME              | VARIABLE_VALUE                         |
+----------------------------+----------------------------------------+
| log_error                  | stderr                                 |
| log_error_services         | log_filter_internal; log_sink_internal |
| log_error_suppression_list |                                        |
| log_error_verbosity        | 2                                      |
+----------------------------+----------------------------------------+
4 rows in set (0.01 sec)

mysql> SHOW TABLES FROM performance_schema LIKE 'error_log';

+------------------------------------------+
| Tables_in_performance_schema (error_log) |
+------------------------------------------+
| error_log                                |
+------------------------------------------+
1 row in set (0.00 sec)
```

`log_error`가 `stderr`이면 실제 수집 위치는 실행 방식에 달려 있다. systemd는 journal로, 컨테이너 런타임은 표준 오류 스트림으로 가져갈 수 있다. 파일 경로만 보고 “로그가 없다”고 결론 내리지 말고 서비스 관리 계층까지 확인해야 한다.

### 3.2 `performance_schema.error_log`로 최근 사건 조회

MySQL 8.0.22 이상에서는 지원되는 구성에서 최근 Error Log를 SQL로 조회할 수 있다. 다음 쿼리는 원문을 무제한 덤프하지 않고 최근 경고와 오류를 좁혀 보여 준다.

```sql
SELECT LOGGED,
       THREAD_ID,
       PRIO,
       ERROR_CODE,
       SUBSYSTEM,
       LEFT(DATA, 160) AS message_excerpt
FROM performance_schema.error_log
WHERE PRIO IN ('Error', 'Warning')
ORDER BY LOGGED DESC
LIMIT 20;
```

실행 결과(MySQL 8.0.x):

```text
mysql> SELECT LOGGED,
    ->        THREAD_ID,
    ->        PRIO,
    ->        ERROR_CODE,
    ->        SUBSYSTEM,
    ->        LEFT(DATA, 160) AS message_excerpt
    -> FROM performance_schema.error_log
    -> WHERE PRIO IN ('Error', 'Warning')
    -> ORDER BY LOGGED DESC
    -> LIMIT 20;

+----------------------------+-----------+---------+------------+-----------+----------------------------------------------------------+
| LOGGED                     | THREAD_ID | PRIO    | ERROR_CODE | SUBSYSTEM | message_excerpt                                          |
+----------------------------+-----------+---------+------------+-----------+----------------------------------------------------------+
| 2026-08-29 00:03:12.354021 |         0 | Warning | MY-011810  | Server    | Insecure configuration for --pid-file ...               |
| 2026-08-29 00:03:12.342363 |         0 | Warning | MY-010068  | Server    | CA certificate ca.pem is self signed.                   |
| 2026-08-29 00:03:11.722590 |         0 | Warning | MY-011068  | Server    | The syntax '--skip-host-cache' is deprecated ...        |
+----------------------------+-----------+---------+------------+-----------+----------------------------------------------------------+
3 rows in set (0.00 sec)
```

위 결과는 검증 환경의 실제 출력에서 메시지 열만 지면에 맞게 줄인 발췌다. 운영 환경에서는 같은 error code의 앞뒤 원문과 반복 횟수를 함께 확인한다.

이 테이블은 중앙 로그 저장소를 대체하지 않는다. 서버 재시작, 보존 한계, sink 구성에 따라 조회 범위가 제한될 수 있다. 장애 발생 시점의 로그를 장기간 보존해야 한다면 파일 수집기, systemd journal 영속화, 컨테이너 로그 드라이버, CloudWatch Logs 같은 외부 저장 계층이 필요하다.

### 3.3 심각도와 subsystem으로 사건 밀도 파악

개별 메시지를 읽기 전에 subsystem별 건수를 집계하면 어느 계층에서 사건이 집중되는지 빠르게 파악할 수 있다.

```sql
SELECT PRIO,
       SUBSYSTEM,
       COUNT(*) AS event_count,
       MIN(LOGGED) AS first_logged,
       MAX(LOGGED) AS last_logged
FROM performance_schema.error_log
GROUP BY PRIO, SUBSYSTEM
ORDER BY FIELD(PRIO, 'Error', 'Warning', 'System', 'Note'),
         event_count DESC,
         SUBSYSTEM;
```

실행 결과(MySQL 8.0.x):

```text
mysql> SELECT PRIO,
    ->        SUBSYSTEM,
    ->        COUNT(*) AS event_count,
    ->        MIN(LOGGED) AS first_logged,
    ->        MAX(LOGGED) AS last_logged
    -> FROM performance_schema.error_log
    -> GROUP BY PRIO, SUBSYSTEM
    -> ORDER BY FIELD(PRIO, 'Error', 'Warning', 'System', 'Note'),
    ->          event_count DESC,
    ->          SUBSYSTEM;

+---------+-----------+-------------+----------------------------+----------------------------+
| PRIO    | SUBSYSTEM | event_count | first_logged               | last_logged                |
+---------+-----------+-------------+----------------------------+----------------------------+
| Warning | Server    |           3 | 2026-08-29 00:03:11.722590 | 2026-08-29 00:03:12.354021 |
| System  | Server    |           4 | 2026-08-29 00:03:11.724251 | 2026-08-29 00:03:12.390574 |
| System  | InnoDB    |           2 | 2026-08-29 00:03:11.731696 | 2026-08-29 00:03:12.100357 |
+---------+-----------+-------------+----------------------------+----------------------------+
3 rows in set (0.00 sec)
```

건수가 많다는 사실만으로 장애라고 판단해서는 안 된다. 서버가 시작될 때 `System`과 `Note`가 집중되는 것은 자연스럽다. 반면 같은 `Error`가 짧은 간격으로 반복되고 재접속·재시작 횟수와 함께 증가한다면 지속 장애일 가능성이 높다.

## 4. Startup 로그 해석

Startup 분석의 목표는 “프로세스가 떴는가”가 아니라 “요청을 안전하게 받을 상태까지 도달했는가”를 판단하는 것이다.

### 4.1 정상 시작의 기준점

정상적인 흐름은 대체로 다음 단계로 구성된다.

1. mysqld 프로세스와 버전 정보 출력
2. 설정 및 데이터 디렉터리 확인
3. InnoDB 초기화
4. 필요하면 crash recovery 수행
5. TLS, plugin, component, scheduler 초기화
6. 포트 또는 socket 리스너 준비
7. `ready for connections`

`ready for connections`는 중요한 기준점이지만 다음을 보장하지는 않는다.

- 모든 애플리케이션 계정의 인증 성공
- Replica SQL thread 정상 동작
- 모든 스키마의 논리적 정합성
- 부하를 견딜 충분한 buffer pool warm-up
- upstream proxy와 service discovery의 정상 전환

따라서 startup 성공 판정은 Error Log 준비 완료 메시지와 함께 실제 연결, 간단한 읽기, Replica 상태, 애플리케이션 health check를 확인해야 한다.

### 4.2 설정 오류 패턴

서버가 InnoDB 초기화 전에 종료되면 다음 범주를 우선 조사한다.

- 알 수 없거나 제거된 시스템 변수
- 값 범위·단위 오류
- 설정 파일 문법 또는 중복 옵션
- 데이터 디렉터리·socket·PID 파일 권한 문제
- 포트 중복 사용
- 필수 component/plugin 로딩 실패
- 인증서·키 파일 접근 실패

운영 절차상 설정 변경은 **정적 검증 → 재시작 전 현재값 기록 → 단일 인스턴스 canary → startup 전체 구간 확인** 순서로 진행한다. 마지막 오류 한 줄만 수정하고 재시작을 반복하면 첫 오류 뒤에 가려져 있던 다른 오류가 차례로 나타나 복구 시간이 길어진다.

## 5. 비정상 종료와 Crash Recovery 패턴

Crash recovery는 InnoDB가 redo log를 이용해 물리적 페이지 상태를 복구하고, commit되지 않은 트랜잭션을 논리적으로 되돌리는 과정이다. recovery 메시지가 있다는 사실은 InnoDB 손상을 뜻하지 않는다. MySQL 프로세스 강제 종료, 호스트 재부팅, OOM kill, 컨테이너 강제 교체처럼 정상 shutdown을 완료하지 못한 뒤에는 정상적으로 수행될 수 있다.

### 5.1 recovery 구간에서 확인할 것

- recovery 시작 시각과 완료 시각
- redo scan 또는 apply 진행이 계속 전진하는지
- rollback 대상 트랜잭션의 규모와 완료 여부
- 동일한 page, tablespace, LSN 관련 오류가 반복되는지
- recovery 직후 다시 프로세스가 종료되는지
- OS에 OOM, I/O error, filesystem read-only, signal 기록이 있는지

redo 적용이 오래 걸린다는 이유만으로 프로세스를 반복 종료하면 복구를 처음부터 다시 수행하거나 추가 장애를 만들 수 있다. CPU, 디스크 읽기/쓰기, Error Log의 진행 지표가 움직이는지 먼저 판단한다. 반대로 동일한 assertion이나 page read failure에서 매번 중단된다면 기다림보다 복제본 승격, 백업 복구, 손상 범위 격리가 우선일 수 있다.

### 5.2 원인과 복구 결과를 분리한다

Crash recovery 성공은 **이번 기동에서 InnoDB가 일관된 상태를 회복했다**는 뜻이지, 이전 비정상 종료의 원인이 해결되었다는 뜻은 아니다. 다음 두 질문을 분리해야 한다.

- 왜 정상 shutdown 없이 프로세스가 사라졌는가?
- 재기동 과정에서 데이터 계층이 정상 복구되었는가?

첫 번째 질문은 kernel log, systemd, container event, hypervisor, 클라우드 이벤트가 답하는 경우가 많다. 두 번째 질문은 InnoDB recovery 로그, 서버 준비 완료, 테이블 접근, Replica 정합성 검증이 답한다.

## 6. Replication 로그 해석

복제 장애는 `Repl` subsystem 또는 Source/Replica 관련 메시지로 나타난다. MySQL 8.0 최신 문서에서는 Source/Replica 용어를 사용하지만, 이전 버전과 일부 메시지·도구에는 Master/Slave 표현이 남아 있을 수 있다.

복제 오류는 다음 세 층으로 나누면 분석이 빨라진다.

| 계층 | 대표 현상 | 우선 확인 |
|---|---|---|
| 연결·수신 | Source 접속 실패, TLS/인증 실패, binary log를 찾지 못함 | DNS·네트워크·계정·인증서·Source 보존 기간 |
| relay log·메타데이터 | relay log 손상, 위치 불일치, repository 오류 | relay log 파일, GTID 상태, 복제 metadata |
| 적용 | duplicate key, row not found, worker/coordinator 중단 | 실패 transaction, 스키마 차이, 병렬 applier worker 상태 |

### 6.1 한 메시지보다 채널과 스레드를 묶어 본다

다중 채널 복제에서는 반드시 channel name을 기준으로 분리한다. 병렬 복제에서는 coordinator가 “중단됨”을 보고하더라도 실제 최초 오류는 worker가 앞서 기록했을 수 있다. Error Log를 역시간순으로만 읽으면 coordinator 메시지를 원인으로 오해할 수 있으므로, 최초 worker error와 관련 transaction 식별자를 찾아야 한다.

운영 대응 시 다음 정보를 한 묶음으로 보존한다.

- channel name
- Source endpoint와 server UUID
- receiver/I/O thread 상태
- coordinator와 worker 상태
- 마지막 적용·수신 GTID
- 실패한 transaction의 error code와 원문
- 장애 전후 DDL·배포·복구 작업

### 6.2 무조건 skip하지 않는다

복제 적용 오류에서 transaction을 건너뛰면 Replica가 다시 움직일 수 있지만, 데이터 불일치를 영구화할 수 있다. duplicate key나 row not found는 대개 “그 한 건이 불필요하다”가 아니라 Source와 Replica 상태가 이미 달라졌다는 신호다. skip 전에 영향 row, 후속 transaction 의존성, GTID 처리, 재동기화 범위를 평가해야 한다.

## 7. InnoDB 경고 패턴

InnoDB 메시지는 범위가 넓으므로 다음 네 부류로 나누어 본다.

### 7.1 일시적 운영 압력

- semaphore wait 또는 장시간 대기
- purge 지연과 history 증가
- checkpoint 또는 flush 압력
- buffer pool resize·load·dump 지연

이 부류는 즉시 손상을 뜻하지 않지만, 반복 빈도와 지속 시간이 증가하면 latency 급등이나 stall로 이어질 수 있다. Error Log와 함께 lock wait, I/O latency, dirty page, redo 생성률, active transaction을 확인한다.

### 7.2 파일과 tablespace 문제

- 필요한 tablespace 파일을 찾지 못함
- 파일 크기·헤더·space ID 불일치
- read-only filesystem 또는 권한 오류
- disk full과 파일 확장 실패

파일을 임의로 복사하거나 이름만 바꾸기 전에 백업과 Replica 상태를 확보한다. `ibdata`, redo, undo, 개별 `.ibd`는 상호 관계가 있으므로 파일 단위 조작이 정합성을 회복한다는 보장이 없다.

### 7.3 페이지 손상과 I/O 오류

checksum mismatch, page corruption, short read, OS I/O error가 반복되면 하드웨어·스토리지·filesystem·메모리 문제까지 조사해야 한다. 단일 page 오류라도 해당 page가 clustered index인지 secondary index인지, 복제본과 백업에서 정상인지에 따라 복구 전략이 달라진다.

- secondary index만 손상되고 table scan이 가능하면 index rebuild를 검토한다.
- clustered index 또는 undo/redo 계층 손상은 논리 dump가 어려울 수 있다.
- 운영 서버에서 `innodb_force_recovery`를 성급히 높이지 않는다. 이 설정은 데이터 구출을 위한 제한 모드이며 정상 운영 복구 수단이 아니다.

### 7.4 설정·호환성 경고

deprecated variable, obsolete option, redo capacity 변경, 파일 형식과 암호화 설정 경고는 당장 기동을 막지 않아도 다음 업그레이드에서 오류로 바뀔 수 있다. 반복 경고를 “늘 있던 로그”로 방치하지 말고 버전 업그레이드 backlog에 연결한다.

## 8. 수집 위치별 확인 방법

### 8.1 systemd 기반 서버

```bash
systemctl status mysqld --no-pager
journalctl -u mysqld --since "2026-08-29 08:30:00" --until "2026-08-29 09:30:00" --no-pager
journalctl -k --since "2026-08-29 08:30:00" --until "2026-08-29 09:30:00" --no-pager
```

마지막 명령은 OOM kill, block device 오류, filesystem 문제처럼 mysqld가 스스로 기록하지 못한 원인을 찾는 데 유용하다. 배포판에 따라 서비스명이 `mysql`일 수 있으므로 실제 unit name을 확인한다.

### 8.2 컨테이너

```bash
docker inspect --format '{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.RestartCount}}' mysql

docker logs --since 60m --timestamps mysql
```

컨테이너 재시작 정책은 서비스 복구에 도움이 되지만 원인을 가릴 수 있다. 현재 로그만 보지 말고 exit code, `OOMKilled`, restart count, 이전 인스턴스 로그 보존 여부를 함께 확인한다. Kubernetes에서는 현재 컨테이너와 직전 컨테이너 로그를 구분해야 한다.

### 8.3 JSON sink와 중앙 수집

구조화 로그를 사용하면 `PRIO`, `err_code`, `subsystem`을 필드로 색인할 수 있다. 운영 환경에서 JSON sink를 활성화할 때는 component 설치 여부, 파일 권한, 수집기 multiline 처리, 기존 텍스트 로그와의 중복 수집을 검증한다.

```text
log_error_services='log_filter_internal; log_sink_json'
```

이 설정은 버전과 배포 패키지에 따라 필요한 component 준비 절차가 다를 수 있다. 운영 서버에 바로 적용하지 말고 동일 버전 검증 환경에서 startup과 rotation, 수집기 파싱을 확인한다.

## 9. 시간축 기반 장애 분석 절차

장애 시각을 `T0`라고 하면 다음 순서가 효과적이다.

1. **T0를 객관화한다.** 사용자 신고 시각이 아니라 최초 오류 응답, 모니터링 변화, 연결 실패 시작 시각을 기록한다.
2. **검색 범위를 넓게 잡는다.** 처음에는 T0 전후 15~30분을 보고, restart나 recovery가 길면 이전 정상 구간까지 확장한다.
3. **서버 생명주기 경계를 표시한다.** process start, recovery start/end, ready, shutdown을 구분한다.
4. **최초 비정상 사건을 찾는다.** 마지막 fatal 메시지가 아니라 그보다 먼저 나타난 I/O, OOM, replication worker, assertion을 찾는다.
5. **반복 주기를 측정한다.** 매초 재접속인지, 매 checkpoint인지, 특정 batch 시간에만 발생하는지 확인한다.
6. **다른 계층과 상관시킨다.** CPU, memory, disk, network, deployment, failover, schema change를 같은 시간축에 겹친다.
7. **복구와 원인 제거를 분리한다.** failover나 restart로 서비스가 돌아와도 원인 가설과 재발 방지 작업은 종료하지 않는다.

```mermaid
sequenceDiagram
    participant App as 애플리케이션
    participant DB as MySQL
    participant OS as OS·컨테이너
    participant Mon as 모니터링

    Mon->>Mon: 최초 지표 이상 시각 T0 기록
    App->>DB: 연결 또는 SQL 요청 실패
    DB-->>App: 오류·timeout
    DB->>DB: Error Log에 subsystem 사건 기록
    OS->>OS: OOM·I/O·signal 사건 기록
    Mon->>DB: Error Log 구간 수집
    Mon->>OS: kernel·runtime event 수집
    Mon->>Mon: 동일 시간축으로 상관 분석
```

## 10. 심각도 판단에서 흔한 오해

### 오해 1: `Warning`은 무시해도 된다

경고가 바로 서비스 중단을 의미하지는 않지만, 인증서 만료 임박, disk space 부족, deprecated option, replication retry처럼 장애의 선행 신호일 수 있다. 반복 빈도와 미래 위험을 평가해야 한다.

### 오해 2: `Error` 한 줄이면 서버 전체 장애다

특정 복제 채널이나 선택적 component의 오류일 수 있다. 영향 범위를 subsystem, channel, thread, 후속 성공 여부로 확인한다.

### 오해 3: 재시작 후 `ready for connections`면 해결되었다

서비스 복구와 원인 제거는 다르다. OOM, disk stall, 잘못된 배포가 남아 있으면 재발한다.

### 오해 4: Error Log timestamp는 애플리케이션 시각과 같다

UTC와 지역 시각, 컨테이너와 호스트, 로그 수집기의 ingest time이 섞일 수 있다. 분석 전에 시각대와 clock skew를 확인한다.

### 오해 5: 메시지 문자열만 경보 조건으로 사용하면 충분하다

문구 변경과 부가 정보 때문에 누락·오탐이 발생한다. 구조화 필드가 있으면 `ERROR_CODE`, `SUBSYSTEM`, `PRIO`를 우선하고 문자열은 보조 조건으로 사용한다.

## 11. 경보와 보존 정책 설계

모든 Error Log를 즉시 호출 경보로 만들면 운영자는 금방 경보를 무시하게 된다. 다음처럼 등급을 나누는 것이 실용적이다.

### 즉시 호출 후보

- mysqld 비정상 종료와 restart loop
- repeated assertion 또는 signal
- page corruption, unrecoverable I/O error
- disk full로 인한 redo·tablespace 확장 실패
- 모든 Replica channel 중단 또는 서비스에 직접 영향을 주는 채널 중단
- crash recovery 반복 실패

### 업무 시간 내 처리 후보

- deprecated option과 업그레이드 호환성 경고
- 일시적 인증 실패 급증
- 단발성 replication reconnect 후 자동 회복
- 인증서·키·용량 관련 사전 경고

경보에는 원문 한 줄만 보내지 말고 다음 문맥을 포함한다.

- server/instance 식별자와 역할
- 발생 시각과 시각대
- error code, subsystem, priority
- 앞뒤 메시지 일부
- 같은 코드의 최근 발생 횟수
- process uptime과 restart count
- 관련 대시보드와 runbook 링크

보존 기간은 장애의 재발 주기보다 길어야 한다. 월말 batch나 분기 작업에서만 재현되는 문제를 7일 로그로 분석할 수는 없다. 원문 로그는 압축 보존하고, 구조화 필드는 장기 검색 인덱스에 유지하는 방식이 비용과 조사 편의의 균형을 맞추기 쉽다.

## 12. Aurora MySQL에서 달라지는 점

Aurora MySQL에서는 호스트 파일시스템에 직접 접근하는 전통적인 방식 대신 DB instance 로그와 AWS 관리 이벤트를 함께 사용한다.

- writer와 각 reader는 별도 Error Log 흐름을 가진다. 장애 시 writer 로그만 보면 reader 승격 전후 문맥을 놓칠 수 있다.
- failover는 MySQL 내부 오류뿐 아니라 Aurora control plane event와 연결해야 한다.
- CloudWatch Logs 내보내기를 사용하면 장기 보존, 검색, Metric Filter, 경보를 구성하기 쉽다.
- 파라미터는 DB cluster parameter group과 DB parameter group 적용 범위를 구분한다.
- storage 계층이 MySQL Community Server와 다르므로 파일 경로·로컬 디스크 복구 절차를 그대로 적용하지 않는다.
- `performance_schema.error_log` 제공 및 보존 특성은 사용하는 Aurora MySQL engine version과 설정에서 직접 확인한다.

Aurora 장애 분석에서는 **DB instance Error Log + RDS event + CloudWatch metric + 애플리케이션 연결 오류 + failover 시각**을 하나의 timeline으로 만든다. 장애 후 새 writer만 조사하면 이전 writer에서 시작된 원인을 잃을 수 있으므로 cluster 전체 인스턴스 로그를 보존해야 한다.

## 13. 장애 대응 체크리스트

### 수집

- [ ] 최초 영향 시각과 시각대를 기록했다.
- [ ] 장애 전후 충분한 범위의 Error Log 원문을 보존했다.
- [ ] process start, recovery, ready, shutdown 경계를 표시했다.
- [ ] systemd·kernel·container·cloud event를 함께 수집했다.
- [ ] 배포, 설정 변경, DDL, failover 이력을 확보했다.

### 해석

- [ ] `PRIO`뿐 아니라 `ERROR_CODE`, `SUBSYSTEM`, thread/channel 문맥을 확인했다.
- [ ] 마지막 오류가 아니라 최초 비정상 사건을 찾았다.
- [ ] 반복 빈도와 진행 여부를 확인했다.
- [ ] startup 성공과 애플리케이션 준비 완료를 구분했다.
- [ ] crash recovery 성공과 비정상 종료 원인 제거를 구분했다.

### 복구

- [ ] restart 전에 데이터와 로그 보존 필요성을 판단했다.
- [ ] recovery 진행 중인 서버를 성급히 반복 종료하지 않았다.
- [ ] 복제 오류에서 영향 분석 없이 transaction을 skip하지 않았다.
- [ ] 손상 의심 시 정상 Replica와 백업을 우선 확보했다.
- [ ] Aurora failover라면 이전 writer를 포함한 모든 관련 인스턴스 로그를 수집했다.

### 재발 방지

- [ ] error code 기반 경보와 반복 횟수 조건을 설계했다.
- [ ] 중앙 수집과 보존 기간이 재발 주기에 충분한지 확인했다.
- [ ] deprecated option과 반복 warning을 작업 항목으로 전환했다.
- [ ] 장애 timeline, 원인, 복구 조치, 검증 결과를 문서화했다.
- [ ] 같은 실패를 조기에 탐지할 health check와 지표를 추가했다.

## 14. 결론

MySQL Error Log를 정확히 읽으려면 한 줄의 문구보다 서버 생명주기와 시간축을 먼저 복원해야 한다. startup에서는 `ready for connections`까지의 전체 흐름을 확인하고, crash에서는 비정상 종료 원인과 InnoDB recovery 결과를 분리한다. replication에서는 channel·receiver·coordinator·worker를 구분하며, InnoDB 경고는 운영 압력, 파일 문제, 페이지 손상, 호환성 경고로 분류한다.

`performance_schema.error_log`와 구조화 필드는 빠른 분류에 유용하지만 장기 보존을 대신하지 않는다. 파일, journal, 컨테이너 runtime, CloudWatch 같은 외부 기록을 같은 시간축에 결합해야 재시작으로 사라진 원인까지 추적할 수 있다. 다음 단계에서는 Error Log와 Performance Schema, OS 지표를 연결해 장애 timeline을 자동으로 구성하는 관측 체계를 다룰 수 있다.
