---
title: "Buffer Pool 개요: 데이터 페이지 캐시가 성능을 좌우하는 이유"
description: "MySQL InnoDB Buffer Pool이 데이터 페이지 캐시, 읽기 I/O, 변경 버퍼링, 체크포인트, 운영 성능에 미치는 영향을 정리한다."
tags: [ MySQL, InnoDB, 성능최적화, 운영 ]
image: "mysql-report-bg.png"
published: "2026-05-31"
updated: "2026-05-31"
author: "MySQL 기술 노트"
source_url: ""
---

## 1. 왜 Buffer Pool을 먼저 이해해야 하는가

MySQL에서 InnoDB 성능을 설명할 때 가장 먼저 보아야 할 영역은 대개 디스크가 아니라 **Buffer Pool**이다. InnoDB는 테이블과 인덱스를 페이지 단위로 관리하고, 실제 SQL 실행 중 자주 접근하는 데이터 페이지와 인덱스 페이지를 메모리에 올려 둔다. 이 메모리 캐시가 Buffer Pool이다.

운영 관점에서 Buffer Pool은 단순한 “캐시” 이상의 의미를 가진다.

- SELECT가 디스크를 읽을지, 메모리에서 끝날지를 결정한다.
- UPDATE/DELETE/INSERT가 변경한 페이지를 즉시 디스크에 쓰지 않고 더티 페이지로 보관한다.
- 체크포인트, redo log, background flush, LRU eviction과 연결되어 쓰기 지연과 순간 I/O를 만든다.
- 인덱스 설계, working set 크기, 배치 작업, 백업, 장애 복구 시간에 직접 영향을 준다.

같은 SQL이라도 필요한 페이지가 Buffer Pool 안에 있으면 CPU와 메모리 접근 중심으로 끝난다. 반대로 매번 디스크에서 페이지를 읽어야 한다면 쿼리 최적화가 잘 되어 있어도 지연 시간이 커지고, 동시성이 증가할수록 I/O 대기가 누적된다. 따라서 InnoDB 성능 문제를 볼 때는 “쿼리가 인덱스를 타는가”와 함께 “그 인덱스와 데이터 페이지가 메모리에 머무를 수 있는가”를 같이 판단해야 한다.

## 2. InnoDB 페이지와 Buffer Pool의 기본 구조

InnoDB는 데이터를 논리적으로 row 단위로 다루지만, 저장과 캐시는 페이지 단위로 처리한다. 기본 InnoDB page size는 16KB다. 테이블의 clustered index page, secondary index page, undo 관련 page, adaptive hash index 관련 구조, change buffer 등 여러 내부 구조가 Buffer Pool과 상호작용한다.

개념적인 흐름은 다음과 같다.

```mermaid
flowchart LR
  SQL[SQL 실행] --> Access[인덱스/데이터 페이지 접근]
  Access --> Hit{Buffer Pool에 페이지 존재?}
  Hit -- Hit --> Memory[메모리 페이지 읽기]
  Hit -- Miss --> Disk[테이블스페이스에서 페이지 읽기]
  Disk --> Load[Buffer Pool에 적재]
  Memory --> Exec[조건 평가/조인/정렬]
  Load --> Exec
  Exec --> Dirty{페이지 변경?}
  Dirty -- 예 --> DirtyPage[Dirty page로 표시]
  DirtyPage --> Redo[Redo log 기록]
  DirtyPage --> Flush[Background flush / checkpoint]
  Dirty -- 아니오 --> Done[문장 완료]
  Flush --> Tablespace[테이블스페이스 반영]
```

Buffer Pool 내부에서는 페이지를 아무렇게나 보관하지 않는다. InnoDB는 LRU에 가까운 리스트, free list, flush list 같은 구조로 페이지를 관리한다. 자주 접근하는 페이지는 오래 남고, 새 페이지를 올릴 공간이 필요하면 덜 유용하다고 판단된 페이지가 밀려난다. 변경된 페이지는 dirty page가 되며, 즉시 디스크에 쓰이지 않고 적절한 시점에 background thread가 flush한다.

이 구조 때문에 Buffer Pool 튜닝은 단순히 `innodb_buffer_pool_size`를 크게 잡는 문제가 아니다. 다음을 함께 고려해야 한다.

1. 실제 working set이 어느 정도인가
2. 읽기 miss가 많은가, 쓰기 flush가 병목인가
3. 배치 작업이 LRU를 오염시키는가
4. 더티 페이지가 너무 쌓여 checkpoint 압력을 만드는가
5. OS page cache와 InnoDB Buffer Pool 사이의 메모리 경계가 적절한가

## 3. Buffer Pool hit와 miss가 성능에 주는 차이

Buffer Pool hit는 필요한 페이지가 이미 메모리에 있다는 뜻이다. 이 경우 InnoDB는 디스크 random I/O 없이 페이지 latch, record 접근, MVCC visibility check, 조건 평가를 진행할 수 있다. 반대로 miss가 발생하면 InnoDB는 tablespace에서 페이지를 읽어 Buffer Pool에 올려야 한다. SSD 환경에서도 메모리 접근과 디스크 접근의 지연 시간 차이는 크며, 동시 요청이 많을 때는 I/O queue와 thread scheduling 비용까지 붙는다.

운영자가 흔히 보는 증상은 다음과 같다.

| 상태 | 관찰되는 현상 | 해석 |
|---|---|---|
| Buffer Pool hit가 높음 | 동일 조회가 안정적으로 빠름 | working set이 메모리에 머무르고 있음 |
| read requests 대비 physical reads 증가 | 특정 시간대에 지연 증가 | 필요한 페이지가 자주 밀려나거나 새 범위를 스캔함 |
| 배치 이후 주요 서비스 쿼리 지연 | 평소 hot page가 밀려남 | 대량 스캔이 LRU를 오염시켰을 가능성 |
| dirty page 비율 증가 | checkpoint/flush 관련 지연 | 쓰기량이 flush 능력을 초과하거나 redo/checkpoint 압력 증가 |

Hit ratio는 유용한 지표지만 절대적인 판정 기준은 아니다. 99% hit ratio라도 1% miss가 매우 큰 범위의 random read라면 지연이 커질 수 있다. 반대로 분석 배치처럼 전체 테이블을 의도적으로 읽는 작업은 hit ratio가 낮아도 문제가 아닐 수 있다. 중요한 것은 workload 특성과 함께 해석하는 것이다.

## 4. `innodb_buffer_pool_size`의 의미와 한계

`innodb_buffer_pool_size`는 InnoDB가 Buffer Pool로 사용할 수 있는 메모리 크기를 정한다. 전용 MySQL 서버에서는 전체 메모리의 상당 부분을 Buffer Pool에 배정하는 것이 일반적이지만, 운영 환경에 따라 다음 요소를 제외하고 판단해야 한다.

- OS와 파일 시스템, 프로세스 기본 메모리
- connection별 sort/join/read buffer 등 세션 메모리
- binary log, relay log, redo log 관련 메모리와 I/O 여유
- Performance Schema, thread stack, temporary table 메모리
- 같은 호스트에 있는 agent, backup, monitoring, sidecar 프로세스

Buffer Pool이 너무 작으면 working set을 담지 못해 physical read가 늘어난다. 너무 크면 OS 메모리 여유가 부족해 swap 위험이 생기거나, 다른 프로세스와 경합한다. 특히 컨테이너 환경에서는 MySQL 설정값과 cgroup memory limit을 함께 보아야 한다. MySQL이 호스트 전체 메모리를 기준으로 튜닝된 상태에서 작은 컨테이너 limit 안에 들어가면 OOM kill 또는 swap성 지연이 발생할 수 있다.

Aurora MySQL에서는 사용자가 로컬 스토리지 파일을 직접 관리하지 않고 분산 스토리지 계층을 사용한다. 그러나 각 DB 인스턴스의 Buffer Pool은 여전히 중요하다. Reader 인스턴스는 writer와 별도의 Buffer Pool을 가지므로, failover 직후나 reader 증설 직후에는 cache warm-up이 충분하지 않아 지연이 일시적으로 증가할 수 있다. Aurora라고 해서 Buffer Pool을 무시할 수 있는 것은 아니다.

## 5. Buffer Pool 상태를 확인하는 기본 SQL

다음 SQL은 MySQL 8.0에서 Buffer Pool 관련 설정과 주요 상태 변수를 확인하는 기본 예제다. 운영 환경에서는 시간 간격을 두고 두 번 이상 측정해 증가율을 봐야 하지만, 먼저 어떤 항목을 볼 수 있는지 확인하는 출발점으로 사용할 수 있다.

```sql
SELECT VERSION() AS mysql_version;

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

SHOW GLOBAL STATUS WHERE Variable_name IN (
  'Innodb_buffer_pool_read_requests',
  'Innodb_buffer_pool_reads',
  'Innodb_buffer_pool_pages_total',
  'Innodb_buffer_pool_pages_free',
  'Innodb_buffer_pool_pages_dirty',
  'Innodb_pages_read',
  'Innodb_pages_written'
);
```

실행 결과(MySQL 8.0.46):

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

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

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| innodb_buffer_pool_size | 67108864 |
+-------------------------+----------+
1 row in set (0.00 sec)

mysql> SHOW GLOBAL STATUS WHERE Variable_name IN (
    ->   'Innodb_buffer_pool_read_requests',
    ->   'Innodb_buffer_pool_reads',
    ->   'Innodb_buffer_pool_pages_total',
    ->   'Innodb_buffer_pool_pages_free',
    ->   'Innodb_buffer_pool_pages_dirty',
    ->   'Innodb_pages_read',
    ->   'Innodb_pages_written'
    -> );

+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| Innodb_buffer_pool_pages_dirty   | 0     |
| Innodb_buffer_pool_pages_free    | 2936  |
| Innodb_buffer_pool_pages_total   | 4096  |
| Innodb_buffer_pool_read_requests | 15356 |
| Innodb_buffer_pool_reads         | 1010  |
| Innodb_pages_read                | 1009  |
| Innodb_pages_written             | 194   |
+----------------------------------+-------+
7 rows in set (0.00 sec)
```

`Innodb_buffer_pool_read_requests`는 논리적 읽기 요청 수이고, `Innodb_buffer_pool_reads`는 Buffer Pool miss로 인해 실제 디스크 읽기가 필요했던 횟수다. 단순 hit ratio는 다음과 같은 방식으로 계산할 수 있다.

```sql
SELECT
  @@innodb_buffer_pool_size AS buffer_pool_size_bytes,
  ROUND(@@innodb_buffer_pool_size / 1024 / 1024, 1) AS buffer_pool_size_mb;

SELECT
  VARIABLE_NAME,
  VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
  'Innodb_buffer_pool_read_requests',
  'Innodb_buffer_pool_reads'
)
ORDER BY VARIABLE_NAME;
```

실행 결과(MySQL 8.0.46):

```text
mysql> SELECT
    ->   @@innodb_buffer_pool_size AS buffer_pool_size_bytes,
    ->   ROUND(@@innodb_buffer_pool_size / 1024 / 1024, 1) AS buffer_pool_size_mb;

+------------------------+---------------------+
| buffer_pool_size_bytes | buffer_pool_size_mb |
+------------------------+---------------------+
|               67108864 |                64.0 |
+------------------------+---------------------+
1 row in set (0.00 sec)

mysql> SELECT
    ->   VARIABLE_NAME,
    ->   VARIABLE_VALUE
    -> FROM performance_schema.global_status
    -> WHERE VARIABLE_NAME IN (
    ->   'Innodb_buffer_pool_read_requests',
    ->   'Innodb_buffer_pool_reads'
    -> )
    -> ORDER BY VARIABLE_NAME;

+----------------------------------+----------------+
| VARIABLE_NAME                    | VARIABLE_VALUE |
+----------------------------------+----------------+
| Innodb_buffer_pool_read_requests | 15356          |
| Innodb_buffer_pool_reads         | 1010           |
+----------------------------------+----------------+
2 rows in set (0.00 sec)
```

위 쿼리의 결과를 바로 “좋다/나쁘다”로 단정하면 안 된다. 상태 변수는 서버 기동 이후 누적값이므로, 운영에서는 다음처럼 일정 시간 차이를 두고 delta를 계산해야 한다.

```sql
DROP TEMPORARY TABLE IF EXISTS bp_sample_1;
DROP TEMPORARY TABLE IF EXISTS bp_sample_2;

CREATE TEMPORARY TABLE bp_sample_1 AS
SELECT VARIABLE_NAME, CAST(VARIABLE_VALUE AS UNSIGNED) AS value
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
  'Innodb_buffer_pool_read_requests',
  'Innodb_buffer_pool_reads',
  'Innodb_pages_read',
  'Innodb_pages_written'
);

CREATE TEMPORARY TABLE bp_sample_2 AS
SELECT VARIABLE_NAME, CAST(VARIABLE_VALUE AS UNSIGNED) AS value
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
  'Innodb_buffer_pool_read_requests',
  'Innodb_buffer_pool_reads',
  'Innodb_pages_read',
  'Innodb_pages_written'
);

SELECT
  s2.VARIABLE_NAME,
  s2.value - s1.value AS delta_value
FROM bp_sample_2 s2
JOIN bp_sample_1 s1 USING (VARIABLE_NAME)
ORDER BY s2.VARIABLE_NAME;
```

실행 결과(MySQL 8.0.46):

```text
mysql> DROP TEMPORARY TABLE IF EXISTS bp_sample_1;

Query OK, 0 rows affected (0.00 sec)

mysql> DROP TEMPORARY TABLE IF EXISTS bp_sample_2;

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TEMPORARY TABLE bp_sample_1 AS
    -> SELECT VARIABLE_NAME, CAST(VARIABLE_VALUE AS UNSIGNED) AS value
    -> FROM performance_schema.global_status
    -> WHERE VARIABLE_NAME IN (
    ->   'Innodb_buffer_pool_read_requests',
    ->   'Innodb_buffer_pool_reads',
    ->   'Innodb_pages_read',
    ->   'Innodb_pages_written'
    -> );

Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> CREATE TEMPORARY TABLE bp_sample_2 AS
    -> SELECT VARIABLE_NAME, CAST(VARIABLE_VALUE AS UNSIGNED) AS value
    -> FROM performance_schema.global_status
    -> WHERE VARIABLE_NAME IN (
    ->   'Innodb_buffer_pool_read_requests',
    ->   'Innodb_buffer_pool_reads',
    ->   'Innodb_pages_read',
    ->   'Innodb_pages_written'
    -> );

Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT
    ->   s2.VARIABLE_NAME,
    ->   s2.value - s1.value AS delta_value
    -> FROM bp_sample_2 s2
    -> JOIN bp_sample_1 s1 USING (VARIABLE_NAME)
    -> ORDER BY s2.VARIABLE_NAME;

+----------------------------------+-------------+
| VARIABLE_NAME                    | delta_value |
+----------------------------------+-------------+
| Innodb_buffer_pool_read_requests |          39 |
| Innodb_buffer_pool_reads         |           0 |
| Innodb_pages_read                |           0 |
| Innodb_pages_written             |           0 |
+----------------------------------+-------------+
4 rows in set (0.00 sec)
```

실제 운영에서는 `SLEEP(60)`을 사이에 두거나 모니터링 시스템에서 1분/5분 rate로 본다. 문서 예제에서는 테스트 시간을 길게 만들지 않기 위해 즉시 두 번 샘플링했다.

## 6. Dirty page와 checkpoint 압력

Buffer Pool은 읽기 캐시이면서 쓰기 버퍼다. UPDATE가 발생하면 InnoDB는 해당 page를 메모리에서 바꾸고 redo log를 기록한 뒤, 페이지 자체는 나중에 tablespace로 flush한다. 이 변경된 메모리 페이지가 dirty page다.

Dirty page가 존재하는 것은 정상이다. 문제는 더티 페이지가 flush 능력보다 빠르게 쌓이거나, checkpoint age가 커져서 InnoDB가 갑자기 aggressive flushing을 해야 하는 상황이다. 이때 애플리케이션에서는 평소보다 commit 지연, write latency 증가, 순간적인 TPS 하락을 볼 수 있다.

운영자는 다음 관계를 함께 본다.

- `Innodb_buffer_pool_pages_dirty`: 현재 더티 페이지 수
- `Innodb_buffer_pool_pages_total`: 전체 Buffer Pool 페이지 수
- `Innodb_pages_written`: 디스크로 쓰인 페이지 누적 수
- redo log capacity와 checkpoint age
- 스토리지 write latency와 IOPS 한계

간단한 dirty page 비율 확인은 다음과 같이 할 수 있다.

```sql
SELECT
  dirty.VARIABLE_VALUE AS dirty_pages,
  total.VARIABLE_VALUE AS total_pages,
  ROUND(
    CAST(dirty.VARIABLE_VALUE AS DECIMAL(20,4)) /
    NULLIF(CAST(total.VARIABLE_VALUE AS DECIMAL(20,4)), 0) * 100,
    2
  ) AS dirty_page_pct
FROM performance_schema.global_status dirty
JOIN performance_schema.global_status total
WHERE dirty.VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty'
  AND total.VARIABLE_NAME = 'Innodb_buffer_pool_pages_total';
```

실행 결과(MySQL 8.0.46):

```text
mysql> SELECT
    ->   dirty.VARIABLE_VALUE AS dirty_pages,
    ->   total.VARIABLE_VALUE AS total_pages,
    ->   ROUND(
    ->     CAST(dirty.VARIABLE_VALUE AS DECIMAL(20,4)) /
    ->     NULLIF(CAST(total.VARIABLE_VALUE AS DECIMAL(20,4)), 0) * 100,
    ->     2
    ->   ) AS dirty_page_pct
    -> FROM performance_schema.global_status dirty
    -> JOIN performance_schema.global_status total
    -> WHERE dirty.VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty'
    ->   AND total.VARIABLE_NAME = 'Innodb_buffer_pool_pages_total';

+-------------+-------------+----------------+
| dirty_pages | total_pages | dirty_page_pct |
+-------------+-------------+----------------+
| 12          | 4096        |           0.29 |
+-------------+-------------+----------------+
1 row in set (0.00 sec)
```

Dirty page 비율이 높다는 사실만으로 장애라고 단정할 수는 없다. 쓰기 workload가 많은 시스템에서는 일정 수준의 dirty page가 자연스럽다. 다만 dirty page가 계속 증가하고, `Innodb_pages_written` 증가율이 낮으며, redo/checkpoint 관련 대기가 증가한다면 flush 병목을 의심해야 한다.

## 7. LRU 오염과 대량 스캔의 영향

Buffer Pool의 크기가 충분해 보여도 대량 테이블 스캔이나 배치 작업이 hot page를 밀어내면 서비스 쿼리 지연이 발생할 수 있다. 예를 들어 낮에는 OLTP 쿼리가 작은 working set을 반복해서 읽고, 새벽에는 통계 배치가 큰 테이블을 순차적으로 훑는 구조라면 배치 직후 주요 인덱스 페이지가 Buffer Pool에서 밀려날 수 있다.

이 현상을 줄이기 위한 접근은 다음과 같다.

1. 배치 SQL이 불필요한 전체 스캔을 하지 않도록 인덱스와 조건을 조정한다.
2. 큰 범위 작업은 chunk 단위로 나누고, 서비스 피크 시간과 겹치지 않게 한다.
3. `innodb_old_blocks_time` 같은 LRU 관련 설정의 의미를 이해하고 조정 가능성을 검토한다.
4. 읽기 전용 분석은 replica 또는 별도 reader로 분리한다.
5. Aurora에서는 reader별 cache 상태가 다르므로 특정 reader에 분석 쿼리가 몰리지 않도록 한다.

단순히 Buffer Pool을 키우는 것만으로는 모든 LRU 오염 문제가 해결되지 않는다. 작업 패턴이 hot working set보다 훨씬 큰 범위를 반복적으로 훑는다면 더 큰 메모리도 빠르게 채워진다. 이 경우 SQL 접근 패턴과 운영 스케줄을 함께 조정해야 한다.

## 8. Buffer Pool warm-up과 재시작 직후 성능

MySQL을 재시작하면 메모리 캐시는 비어 있는 상태에서 출발한다. InnoDB는 Buffer Pool dump/load 기능을 통해 종료 전 hot page 정보를 저장하고 재시작 후 일부를 다시 로드할 수 있다. 관련 설정은 `innodb_buffer_pool_dump_at_shutdown`, `innodb_buffer_pool_load_at_startup`, `innodb_buffer_pool_dump_pct` 등이다.

하지만 warm-up이 모든 문제를 없애지는 않는다.

- dump 대상은 page 내용 전체가 아니라 page identifier 목록이다.
- 재시작 직후 실제 page load에는 시간이 걸릴 수 있다.
- workload가 바뀌었으면 이전 hot page가 현재 hot page가 아닐 수 있다.
- failover된 Aurora reader/writer는 기존 인스턴스와 cache 상태가 다르다.

따라서 재시작, failover, version upgrade, parameter 변경 후에는 “DB가 떴다”만 볼 것이 아니라 주요 쿼리 latency와 Buffer Pool read miss 증가율을 일정 시간 관찰해야 한다.

## 9. 흔한 오해와 주의점

### 9.1 Hit ratio만 높으면 안전하다는 오해

Buffer Pool hit ratio는 전체 누적값이므로 최근 장애를 희석할 수 있다. 서버가 며칠 동안 안정적으로 운영되어 hit ratio가 높게 누적된 상태에서, 특정 배치가 10분 동안 많은 physical read를 일으켜도 전체 비율은 크게 변하지 않을 수 있다. 운영에서는 누적값보다 rate와 시간대별 변화를 보아야 한다.

### 9.2 메모리는 무조건 크게 잡으면 된다는 오해

Buffer Pool이 크면 유리하지만, OS와 세션 메모리까지 압박하면 더 큰 문제가 된다. swap이 발생하면 InnoDB가 의도한 메모리 캐시 구조가 무너진다. 특히 MySQL이 단독으로 쓰지 않는 서버, 컨테이너, Kubernetes, 백업 에이전트가 함께 있는 환경에서는 메모리 여유를 보수적으로 남겨야 한다.

### 9.3 Aurora에서는 Buffer Pool이 덜 중요하다는 오해

Aurora는 스토리지 계층이 다르지만 SQL 실행은 여전히 DB 인스턴스의 메모리와 CPU 위에서 이루어진다. Reader별 cache 차이, failover 후 cache cold 상태, scale-out 직후 warm-up 문제는 Aurora 운영에서 실제로 중요하다.

### 9.4 Full table scan은 항상 나쁘다는 오해

전체 스캔 자체가 항상 나쁜 것은 아니다. 작은 테이블, 분석 목적, 순차 I/O에 적합한 배치에서는 합리적일 수 있다. 문제는 OLTP hot set과 같은 Buffer Pool을 공유하면서 큰 스캔이 반복되어 서비스 쿼리의 캐시 효율을 떨어뜨리는 경우다.

## 10. 운영 점검 체크리스트

- [ ] `innodb_buffer_pool_size`가 서버 또는 컨테이너 메모리 한계에 비해 과도하지 않은가
- [ ] 주요 업무 시간대의 `Innodb_buffer_pool_reads` 증가율이 평소보다 커지지 않는가
- [ ] hit ratio가 아니라 1분/5분 단위의 physical read rate를 보고 있는가
- [ ] 배치, 백업, 리포트 쿼리가 OLTP working set을 밀어내지 않는가
- [ ] dirty page 비율과 page written 증가율이 쓰기 workload와 균형을 이루는가
- [ ] 재시작 또는 failover 이후 cache warm-up 시간을 운영 절차에 반영했는가
- [ ] Aurora reader별 Buffer Pool 상태 차이를 고려해 트래픽을 분산하고 있는가
- [ ] 성능 장애 분석 시 SQL 실행 계획과 Buffer Pool miss를 함께 보고 있는가

## 11. 결론

Buffer Pool은 InnoDB 성능의 중심에 있는 메모리 계층이다. 인덱스가 아무리 잘 설계되어 있어도 필요한 페이지가 계속 디스크에서 읽히면 지연은 커진다. 반대로 working set이 Buffer Pool 안에 안정적으로 머무르면 같은 하드웨어에서도 훨씬 높은 처리량과 낮은 지연 시간을 얻을 수 있다.

운영자는 Buffer Pool을 단순히 큰 캐시로 보지 말고, 페이지 수명, miss, dirty page, checkpoint, 배치 작업, 재시작 후 warm-up까지 포함한 동적 시스템으로 보아야 한다. 다음 단계의 InnoDB 성능 분석에서는 이 Buffer Pool 위에서 인덱스 접근, clustered index, secondary index, redo/undo, flush 정책이 어떻게 연결되는지 계속 이어서 살펴볼 수 있다.
