데이터 압축의 필요성
MySQL 서버에서 디스크에 저장된 데이터 파일의 크기는 일반적으로 쿼리의 처리 성능, 백업 및 복구 시간과 밀접하게 연결된다.
- 디스크의 데이터 파일이 크면 클수록 쿼리를 처리하기 위해 더 많은 데이터 페이지를 InnoDB 버퍼 풀로 읽어야 하고, 새로운 페이지가 버퍼 풀로 적재되기 때문에 그만큼 더티 페이지가 더 자주 디스크로 기록돼야 한다.
- 백업 및 복구 시간이 늘어나고, 그만큼의 저장 공간이 필요하기 때문에 비용 문제도 있을 수 있다.
MySQL 서버에서 사용 가능한 압축 방식은 크게 테이블 압축과 페이지 압축으로 구분 가능하다.
6.1 페이지 압축
페이지 압축은 "Transparent Page Compression" 라고도 불리는데, MySQL 서버가 디스크에 저장하는 시점에 데이터 페이지가 압축되어 저장되고, 반대로 MySQL 서버가 디스크에서 데이터 페이지를 읽어올 때 압축이 해제되기 때문이다. 즉, 버퍼 풀에 데이터 페이지가 한 번 적재되면 InnoDB 스토리지 엔진은 압축이 해제된 상태로만 데이터 페이지를 관리한다. 그래서 MySQL 서버의 내부 코드에서는 압축 여부와 관계없이 "투명(Transparent)"하게 작동한다.
페이지 압축에서 중요한 점은 16KB 데이터 페이지를 압축한 결과의 용량이 얼마나 될지 예측할 수 없더라도 버퍼 풀에서 하나의 테이블은 동일한 크기의 페이지(블록)로 통일돼야 한다는 것이다.
- 펀치 홀(Punch hole): 운영체제별로 특정 버전의 파일 시스템에서만 지원되는 기능
MySQL 서버는 특정 테이블에 대해 16KB 크기의 페이지를 유지하면서도 압축된 다양한 크기의 데이터 페이지를 디스크에 저장하고 압축된 만큼의 공간을 절약할 수 있다.

1. 16KB 페이지를 압축 (압축 결과를 7KB로 가정)
2. MySQL 서버는 디스크에 압축된 결과 7KB를 기록
(이때 MySQL 서버는 압축 데이터 7KB + 9KB의 빈 데이터를 기록)
3. 디스크에 데이터를 기록한 후, 7KB 이후의 공간 9KB에 대해 펀치 홀(punch-hole)을 생성
4. 파일 시스템은 7KB만 남기고 나머지 디스크의 9KB 공간은 다시 운영체제로 반납
MySQL 서버의 페이지 압축이 가진 문제
- 펀치 홀 기능은 운영체제 뿐만 아니라 하드웨어 자체에서도 해당 기능을 지원해야 사용 가능하다.
- 아직 파일 시스템 관련 명령어(유틸리티)가 펀치 홀을 지원하지 못한다.
MySQL 서버의 데이터 파일은 해당 서버에만 머무는 것이 아니라 백업했다가 복구하는 과정에서 데이터 파일 복사 과정이 실행되고, 그 외에도 많은 파일 관련 유틸리티들을 사용한다. 예를 들어, 펀치 홀이 적용되어 실제 데이터 파일의 크기가 1GB라고 하더라도 "cp" 같은 파일 복사 명령 또는 XtraBackup 같은 툴이 파일을 복사하면 펀치 홀이 다시 채워져서 데이터 파일의 크기는 원본 크기인 10GB가 될 수도 있다.
이런 이유로 실제로 페이지 압축은 많이 사용되지 않는다.
페이지 압축을 이용하기 위해서는 테이블을 생성하거나 변경할 때 다음과 같이 COMPRESSION 옵션을 설정하면 된다.
zlib 와 lz4 알고리즘을 지원한다.
-- // 테이블 생성 시
mysql> CREATE TABLE t1 (c1 INT) COMPRESSION="zlib";
-- // 테이블 변경 시
mysql> ALTER TABLE t1 COMPRESSION="zlib";
mysql> OPTIMIZE TABLE t1;
6.2 테이블 압축
테이블 압축은 운영체제나 하드웨어에 대한 제약 없이 사용할 수 있기 때문에 일반적으로 더 활용도가 높다. 테이블 압축은 디스크의 데이터 파일 크기를 줄일 수 있기 때문에 그만큼의 이득이 있지만, 몇 가지 단점도 있다.
- 버퍼 풀 공간의 활용률이 낮음
- 쿼리 처리 성능이 낮음
- 빈번한 데이터 변경 시 압축률이 떨어짐
이러한 단점들이 발생하는 이유를 이해하려면 내부적으로 어떻게 압축이 실행되어 디스크에 저장되는지, 그리고 압축된 데이터 페이지들이 버퍼 풀에 어떻게 적재되어 사용되는지를 이해해야 한다.
압축 테이블 생성
압축 테이블 생성
- innodb_file_per_table 시스템 변수 ON 으로 설정
- 테이블 압축을 사용하기 위해서는 압축을 사용하려는 테이블이 별도의 테이블 스페이스를 사용해야 한다.
- 테이블 생성과 설정
- ROW_FORMAT=COMPRESSED 옵션 명시
- KEY_BLOCK_SIZE 을 이용해 압축된 페이지의 타깃 크기(목표 크기) 명시. 2n(n>=2)으로만 설정할 수 있다. InnoDB 스토리지 엔진의 페이지 크기(innodb_page_size) 이상으로 설정하면 테이블 압축을 적용할 수 없다.
mysql> SET GLOBAL innodb_file_per_table=ON;
-- // ROW_FORMAT 옵션과 KEY_BLOCK_SIZE 옵션을 모두 명시
mysql> CREATE TABLE compressed_table (
c1 INT PRIMATY KEY
)
ROW_FORMAT=COMPRESSED
KEY_BLOCK_SIZE=8;
-- //KEY_BLOCK_SIZE 옵션만 명시 -> 자동으로 ROW_FORMAT=COMPRESSED 옵션 추가
mysql> CREATE TABLE compressed_table (
c1 INT PRIMATY KEY
)
KEY_BLOCK_SIZE=8;
- KEY_BLOCK_SIZE: 압축된 페이지가 저장될 페이지의 크기
데이터 페이지를 압축한 용량이 얼마가 될지 알 수 없는데, 어떻게 KEY_BLOCK_SIZE 를 테이블을 생성할 때 설정할 수 있을까?
InnoDB 스토리지 엔진의 테이블 압축 작동 방식
1. 16KB 의 데이터 페이지를 압축 ( KEY_BLOCK_SIZE = 8 )
1.1 압축된 결과가 8KB 이하이면 그대로 디스크에 저장(압축 완료
1.2. 압축된 결과가 8KB 를 초과하면 원본 페이지를 스플릿(split)해서 2개의 페이지에 8KB 씩 저장
2. 나뉜 페이지 각각에 대해 1번 단계를 반복 실행

테이블 압축에서는 InnoDB I/O 레이어에서는 아무런 역할을 하지 않는다는 것을 알 수 있다.
테이블 압축 방식에서 가장 중요한 것은 원본 데이터 페이지의 압축 결과가 목표 크기(KEY_BLOCK_SIZE)보다 작거나 같을 때까지 반복해서 페이지를 스플릿하는 것이다. 그래서 목표 크기가 잘못 설정되면 MySQL 서버의 처리 성능이 급격히 떨어질 수 있음을 주의하자.
KEY_BLOCK_SIZE 결정
테이블 압축에서 가장 중요한 부분은 압축된 결과가 어느 정도가 될지를 예측해서 KEY_BLOCK_SIZE 를 결정하는 것이다. 그래서 테이블 압축을 적용하기 전에 먼저 KEY_BLOCK_SIZE 를 4KB 또는 8KB 로 테이블을 생성해서 샘플 데이터를 저장해보고 적절한지 판단하는 것이 좋다. 샘플 데이터는 많으면 많을수록 정확한 테스트가 가능한데, 데이터 페이지가 최소 10개는 생성되도록 테스트 데이터를 INSERT 하는 것이 좋다.
테스트 할 때 innodb_cmp_per_index_enabled 시스템 변수를 ON 으로 하면 인덱스별로 압축 실행 횟수와 성공 횟수를 기록할 수 있다. ( information_schema.INNODB_CMP_PER_INDEX 테이블 )

압축 횟수(compress_ops) - 압축 성공 횟수(compress_ops_ok) = 압축 실패 횟수
이는 압축 결과가 KEY_BLOCK_SIZE 를 초과하여 데이터 페이지를 스플릿해서 다시 압축을 실행한 횟수를 의미한다.
일반적으로 압축 실패율(compression_failure_pct)은 3~5% 미만으로 유지할 수 있게 KEY_BLOCK_SIZE 를 선택하는 것이 좋다.

KEY_BLOCK_SIZE 를 8KB로 설정했음에도 불구하고 PRIMARY 키의 압축 실패율이 꽤 높게 나타났다. 이 결과를 기준으로 판단한다면 이 결과를 기준으로 판단한다면, 압축을 적용하면 압축 실패율이 꽤 높아서 InnoDB 버퍼 풀에서 디스크로 기록되기 전에 압축하는 과정에서 꽤 오랜 시간이 걸릴 것이라고 예측할 수 있다. 성능에 민감한 서비스라면 employees 테이블은 압축을 적용하지 않는 것이 좋다고 판단할 수 있다.

예제의 경우 4KB 와 8KB 압축의 결과가 거의 차이가 나지 않는다. 이 경우 압축을 선택해야 한다면 압축 실패율은 낮으면서 압축 효율은 상대적으로 높은 8KB 를 선택하는 것이 효율적일 것이다.
조회와 변경 횟수가 낮을수록 압축의 효용성이 높다. (ex. INSERT 만 되는 로그 테이블)
압축 알고리즘은 많은 CPU자원을 소모하기 때문에 필요한 경우에만 사용하는 것이 좋다.
압축된 페이지의 버퍼 풀 적재 및 사용
InnoDB 스토리지 엔진은 압축된 테이블의 데이터 페이지를 버퍼 풀에 적재하면
- 압축된 상태
- 압축이 해제된 상태
의 2개 버전을 관리한다. 따라서 LRU 리스트도 압축된 페이지와 압축되지 않은 페이지를 모두 가질 수 있다.
- LRU 리스트: 디스크에서 읽은 상태 그대로의 데이터 페이지 관리 (압축 테이블에서 읽어왔다면 압축된 데이터)
- Uuzip_LRU 리스트: 압축된 페이지들의 압축 해제 버전 관리 (애초에 압축이 적용되지 않은 데이터는 관련 없음)
문제점
- 압축된 테이블에 대해 버퍼 풀의 공간을 이중으로 사용하여 메모리를 낭비한다.
- 압축된 페이지에서 데이터를 읽거나 변경하기 위해서는 압축을 해제해야 하는데, 압축 및 압축 해제 작업은 CPU를 상대적으로 많이 소모한다.
요청 패턴에 따른 해결 방안
- InnoDB 버퍼 풀의 공간이 필요한 경우에는 LRU 리스트에서 원본 데이터 페이지(압축된 형태)는 유지하고, Unzip_LRU 리스트에서 압축 해제된 버전은 제거해서 버퍼 풀의 공간을 확보한다.
- 압축된 데이터 페이지가 자주 사용되는 경우에는 Unzip_LRU 리스트에 압축 해제된 페이지를 계속 유지하면서 압축 및 압축 해제 작업을 최소화한다.
- 압축된 데이터 페이지가 사용되지 않아서 LRU 리스트에서 제거되는 경우에서는 Unzip_LRU 리스트에서도 함께 제거된다.
InnoDB 스토리지 엔진은 버퍼 풀에서 압축 해제된 버전의 데이터 페이지를 적절한 수준으로 유지하기 위해 다음과 같은 Adaptive 알고리즘을 사용한다.
- CPU 사용량이 높은 서버에서는 가능하면 압축과 압축 해제를 피하기 위해 Unzip_LRU 의 비율을 높여서 유지하고
- Disk IO 사용량이 높은 서버에서는 가능하면 Unzip_LRU 리스트의 비율을 낮춰서 InnoDB 버퍼 풀의 공간을 더 확보하도록 록 작동한다.
'Book > Real MySQL 8.0 上' 카테고리의 다른 글
| 08. 인덱스-1 (0) | 2022.12.11 |
|---|---|
| 07. 데이터 암호화 (0) | 2022.12.10 |
| 05. 트랜잭션과 잠금 (0) | 2022.12.02 |
| 04. 아키텍처 (0) | 2022.11.27 |
| 02. 설치와 설정 (0) | 2022.09.21 |