Skip to content

Commit

Permalink
docs: Real MySQL 8.0 6장 데이터 압축
Browse files Browse the repository at this point in the history
  • Loading branch information
jmxx219 committed Dec 18, 2024
1 parent 82a09bd commit c5caec5
Showing 1 changed file with 180 additions and 0 deletions.
180 changes: 180 additions & 0 deletions 도서/Real MySQL 8.0/6. 데이터 압축.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# 6. 데이터 압축

### 목차

- [6.1 페이지 압축](#61-페이지-압축transparent-page-compression)
- [6.2 테이블 압축](#62-테이블-압축)

<br>

> MySQL 서버에서 디스크에 저장된 데이터 파일의 크기는 일반적으로 **쿼리의 처리 성능**과도 직결되지만 **백업 및 복구 시간**과도 밀접하게 연결된다.
>
> 디스크의 데이터 파일의 크기가 크면?
> - 쿼리를 처리하기 위해 더 많은 페이지를 InnoDB 버퍼 풀에서 읽어야 할 수 있다.
> - 새로운 페이지가 버퍼 풀로 적재되기 때문에 그만큼 더티 페이지가 더 자주 디스크로 기록되어야 한다.
> - 백업 믹 복구 시간이 오래 걸린다. 또한, 그만큼 저장 공간이 필요하기 때문에 비용 문제도 발생한다.
>
> 많은 DBMS가 이런 문제점을 해결하기 위해 데이터 압축 기능을 제공한다.
> - MySQL 서버에서는 데이터 압축 방식은 크게 `테이블 압축``페이지 압축`으로 나뉘어진다.
<br>

## 6.1 페이지 압축(Transparent Page Compression)

### 압축 방식

- 디스크에 저장하는 시점에 데이터 페이지가 압축되어 저장되고, 디스크에서 읽어올 때 압축 해제된다.
- 즉, 버퍼 풀에 데이터 페이지가 한 번 적재되면 InnoDB 스토리지 엔진은 압축이 해제된 상태로만 데이터 페이지를 관리한다.
- 그래서 MySQL 서버의 내부 코드에서는 압축 여부와 관계없이 `투명(Tranparen)` 하게 작동한다.
- 문제점
- 데이터 페이지를 압축한 결과가 용량이 얼마나 될지 예측이 불가능하다.
- 적어도 하나의 테이블은 동일한 크기의 페이지(블록) 로 통일돼야 한다.
- 그래서 페이지 압축 기능은 `펀치 홀` 기능을 사용한다.

<br>

### 펀치홀(Punch-hole)

<img width="450" src="https://github.com/user-attachments/assets/ee83a657-8e81-44f5-8899-791b576c2c0b" />

```
1. 16KB 페이지를 압축 (압축 결과를 7KB로 가정)
2. MySQL 서버는 디스크에 압축된 결과 7KB를 기록 (이때 MySQL 서버는 압축 데이터 7KB에 9KB의 빈 데이터를 기록)
3. 디스크에 데이터를 기록한 후, 7KB 이후의 공간 9KB에 대해 펀치 홀을 생성
4. 파일 시스템은 7KB만 남기고 나머지 디스크의 9KB 공간은 다시 운영체제로 반납
```
- 실제 디스크 공간의 7KB만 차지하지만, 운영체제에서 읽으면 압축된 데이터 7KB와 펀치 홀 공간인 9KB를 합쳐서 16KB를 읽는다.


- 문제점
- 운영체제뿐만 아니라 하드웨어 자체에서도 해당 기능을 지원해야 사용 가능하다는 점
- 아직 파일 시스템 관련 명령어가 펀치홀을 지원하지 못한다.
- 복사 및 복제하는 과정에서 펀치 홀이 다시 채워져서 데이터 파일의 크기가 원본 크기가 될 수 있다.
- 이러한 이유로 실제 페이지 압축을 많이 사영되지 않는 상태이다.


<br>
<br>

## 6.2 테이블 압축

- 테이블 압축은 페이지 압축과 달리 운영체제나 하드웨어의 제약 없이 사용할 수 있기 때문에 활용도가 더 높다.
- 디스크의 데이터 파일 크기를 줄일 수 있라는 이득이 있다.
- 하지만 몇 가지 단점도 있다.
- 버퍼 풀 공간 활용률이 낮음
- 쿼리 처리 성능이 낮음
- 빈번한 데이터 변경 시 압축률이 떨어짐

<br>

### 6.2.1 압축 테이블 생성

#### 전체 조건

```sql
CREATE TABLE COMPRESSED_TABLE(
C1 INT PRIMARY KEY,
)
ROW_FORMAT=COMPRESSED
KEY_BLOCK_SIZE=8;
```

- 테이블 압축을 사용하려면 압축을 사용하려는 테이블이 별도의 테이블 스페이스를 사용해야 한다. (`innodb_file_per_table` 시스템 변수 ON 상태로 생성)
- 그리고 압축 테이블을 생성할 때 `ROW_FORMAT=COMPRESSED` 옵셥을 설정하고, `KEY_BLOCK_SIZE` 옵션을 이용해 압축된 페이지의 타깃 크기를 명시한다.
- `KEY_BLOCK_SIZE` 옵션은 압축된 페이지가 저장될 페이지의 크기를 지정한다.
- `KEY_BLOCK_SIZE`는 2n(n >= 2)으로만 설정할 수 있다.
- InnoDB 스토리지 엔진의 페이지 크기(`innodb_page_size`)가 16KB 라면 4KB 또는 8KB로만 설정할 수 있다.

<br>

#### 압축 방식

<img width="300" src="https://github.com/user-attachments/assets/dea35d6b-dc40-4cae-8b48-2c31e558d368" />

```
1. 16KB 의 데이터 페이지를 압축
1.1 압축된 결과가 8KB 이하이면 그대로 디스크에 저장 ( 압축 완료 )
1.2 압축된 결과가 8KB 를 초과하면 원본 페이지를 스플릿 (split)해서 2 개의 페이지에 8KB 씩 저장
2. 나뉜 페이지 각각에 대해 1" 번 단계를 반복 실행
```

- 원본 데이터 페이지의 압축 결과가 목표 크기(`KEY_BLOCK_SIZE`)보다 작거나 같을 때까지 반복해서 페이지를 스플릿한다.
- 그래서 목표 크기가 잘못 설정되면 MySQL 서버의 처리 성능이 급격히 떨어질 수 있으니 주의해야 한다.

<br>

### 6.2.2 KEY_BLOCK_SIZE 결정

> 테이블 압축에서 가장 중요한 부분이 압축된 결과가 어느 정도가 될지 예측해서 KEY_BLOCK_SIZE를 결정하는 것이다.
> 그래서 테이블을 압축하기 전에 KIB 또는 8KIB 로 테이블을 생성해서 샘플 데이터를 저장해보고 적절한지 판단하는 것이 좋다.
```sql
mysql> USE employees;

-- // 테이블 압축을 사용하는 예제 테이블을 생성
mysql> CREATE TABLE employees_comp4k (
emp_no int NOT NULL,
birth_date date NOT NULL,
first_name varchar (14) NOT NULL,
last_name varchar (16) NOT NULL,
gender enum( 'M', 'F') NOT NULL,
hire_date date NOT NULL,
PRIMARY KEY (emp_no),
KEY ix_firstname (first_name),
KEY ix_hiredate (hire_date)
) ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;

-- // 테스트를 실행하기 전에 innodb_cmp_per_index_enabled 시스템 변수를 ON 으로 변경해야 인덱스별로 압축 실행 횟수와 성공 횟수가 기록된다.
mysql> SET GLOBAL innodb_mp_per_index_enabled=ON;

-- // employees 테이블의 데이터를 그대로 압축 테스트 테이블로 저장
mysql> INSERT INTO employees_comp4k SELECT * FROM employees;

-- // 인덱스별로 압축 횟수와 성공 횟수, 압축 실패율 조회
mysql > SELECT
table_name, index_name, compress_ops, compress_ops_ok,
(compress_ops-compress_ops_ok)/compress_ops* 100 as compression_failure_pct
FROM information_schema. INNODB_CMP_PER_INDEX;
```

- 일반적으로 압축 실패율은 3~5% 미만으로 유지할 수 있게 `KEY_BLOCK_SIZE`를 선택하는 것이 좋다.
- 압축 실패율이 높다고 해서 압축을 사용하지 말아야 하는 것은 아니다.
- INSERT만 되는 로그 테이블의 경우, 한번 INSERT되면 이후에 변경되지 않기 때문에 재압축하더라도 파일의 크기가 큰 폭으로 줄어든다면 큰 손해는 아니다.
- 반대로 압축 실패욜이 높지 않은 경우라고 하더라도 테이블의 데이터가 매우 빈번하게 조회되고 변경된다면 압축은 고려ㅏ지 않는 것이 좋다.
- 압축 알고리즘은 많은 CPU 자원을 소모한다.

<br>

### 6.2.3 압축된 페이지의 버퍼 풀 적재 및 사용

- InnoDB는 압축된 테이블의 데이터 페이지를 버퍼 풀에 적재하면 압축된 상태와 압축이 해제된 상태 2개 버전을 관리한다.
- 그래서 InnoDB 스토리지 엔진은 압축된 그대로의 데이터 페이지를 관리하는 `LRU 리스트`와 압축이 해제된 페이지를 관리하는 `Unzip_LRU 리스트`를 별도로 관리한다.
- 문제점
- 압축된 테이블에 대해서는 버퍼 풀의 공간을 이중으로 사용함으로써 메모리를 낭비하는 효과가 있다.
- 또한, 압축된 페이지에서 데이터를 읽거나 변경하기 위해 압축을 해제해야 하는데, 이러한 작업은 CPU를 상대적으로 많이 소모한다.
- 이러한 두 가지 단점을 보완하기 위해 `Unzip_LRU 리스트`를 별도로 관리하고 있다가 MySQL 서버로 유입되는 요청 패턴에 따라서 적절히(`Adaptive`) 처리를 수행한다.
- InnoDB 버퍼 풀 공간이 필요한 경우
- LRU 리스트에서 원본 데이터 페이지(압축된 형태)는 유지하고, Unzip_LRU 리스트에서 압축 해제된 버전은 제거해서 버퍼 풀 공간을 확보한다.
- 압축된 데이터 페이지가 자주 사용되는 경우
- Unzip_LRU 리스트에 압축 해제된 페이지를 계속 유지하면서 압축 및 압축 해제 작업을 최소화한다.
- 압축된 데이터 페이지가 사용되지 않아 LRU 리스트에서 제거되는 경우
- Unzip_LRU 리스트에서도 함께 제거한다.

#### 어댑티브(적응적인) 알고리즘
- 버퍼 풀에서 압축 해제된 버전의 데이터 페이지를 적절한 수준으로 유지하는 방법
- CPU 사용량이 높은 서버 → 압축과 압축 해제를 피하기 위해 Unzip_LRU의 비율 높이기
- Disk IO 사용량이 높은 서버 → InnoDB 버퍼 풀의 공간을 더 확보하기 위해 Unzip_LRU의 비율 낮추기

<br>

### 6.2.4 테이블 압축 관련 설정

> 테이블 압축을 사용할 때 연관된 시스템 변수가 몇 가지 있는데, 모두 페이지의 압축 실패율을 낮추기 위해 필요한 튜닝 포인트를 제공한다.
- `innodb_cmp_per_index_enabled`
- `innodb_compression_level`
- `innodb_compression_failure_threshold_pct`, `innodb_compression_pad_pct_max`
- `innodb_log_compressed_pages`

<br>
<br>

0 comments on commit c5caec5

Please sign in to comment.