[Real MySQL] 6. 데이터 압축

@lim · October 05, 2024

데이터 파일의 크기가 커지면 발생 할 수 있는 문제들

  • 쿼리 처리 성능 증가
  • 백업 및 복구 시간 증가
  • 저장 공간 사용에 따른 비용 증가
  • InnoDB 버퍼 풀에 더 많은 데이터 페이지 로드 필요
  • 더티 페이지의 잦은 디스크 기록

→ 압축을 활용할 수 있음

압축 장점

  • 디스크 공간 절약
  • I/O 감소로 성능 향상 가능
  • 메모리 사용량 감소

압축 단점

  • CPU 사용량 증가
  • 데이터 수정 시 압축/해제 오버헤드 발생
  • 경우에 따라 성능 저하 발생 가능

MySQL 압축 방식

  • 페이지 압축
  • 테이블 압축

데이터 페이지(Page)

  • MySQL에서 데이터를 저장하는 기본 단위
  • 디스크 ↔ 메모리 간 데이터 이동 기본 단위
  • InnoDB 가능 페이지 크기 : 4KB, 8KB, 16KB (기본값), 32KB, 64KB

페이지 압축 (Transparent Page Compression)

MySQL이 데이터 페이지를 저장할 시점에 압축하고, 읽을 시점에 압축 해제하는 방식

버퍼 풀에는 항상 압축 해제된 상태로 관리 (InnoDB 기준)

MySQL에서는 압축 여부와 상관없이 데이터를 투명(Transparent)하게 처리

→ 스토리지 엔진은 압축이 해제된 상태로만 데이터 페이지를 처리

압축 알고리즘은 zlib, lz4 지원

  • zlib : 높은 압축률, 상대적으로 느림
  • lz4 : 낮은 압축률, 빠른 처리

특징

  • 동일 테이블 내에서는 같은 크기의 페이지 블록 유지 필요
  • 디스크 공간 절약 가능
  • 압축된 크기를 사전에 예측하기 어려움

작동 방식

  • 16KB 크기의 데이터 페이지를 압축하여 디스크에 저장
    • 그러나 압축 된 16KB 데이터 페이지 용량을 예측하기 어려움

      → 운영체제의 펀치 홀(Punch hole) 기능을 이용

      예 : 16KB의 페이지를 7KB로 압축되면, 나머지 9KB는 펀치 홀로 처리

압축 테이블 사용

테이블을 생성할 때 COMPRESSION 옵션을 사용

테이블 변경시에도 적용 가능하나 설정 이후 발생하는 테이블스페이스에 대한 쓰기에만 적용

기존 페이지에도 적용하려면 OPTIMIZE TABLE 을 사용하여 테이블을 재작성

COMPRESSION 옵션 값 : zlib , lz4 , none

CREATE TABLE t1(no int) COMPRESSION="zlib";
CREATE TABLE t1(no int) COMPRESSION="lz4";
CREATE TABLE t1(no int) COMPRESSION="none";

ALTER TABLE t1 COMPRESSION="zlib";
OPTIMIZE TABLE t1;

한계

  • 펀치 홀(Punch hole)을 지원하는 운영체제, 파일 시스템, 하드웨어 필요
    • Windows : NTFS 파일 시스템을 사용할 경우
    • Linux : 펀치 홀을 지원하는 일정 커널 버전 이상
  • 다수의 파일 시스템 유틸리티에서 펀치 홀 미지원
  • 파일 복사 시(예 : cp 명령어) 펀치 홀이 다시 채워짐
    • 백업 및 복구 과정에서 문제 발생 가능

리눅스 커널이 3.10 이상부터는 대부분 지원 → 요즘 Ubuntu, RHEL(센트, 록키)에서는 거의 모두 지원

지원 버전 확인은 MySQL 문서에서 확인 (링크)


테이블 압축

테이블 압축은 페이지 압축과 달리 MySQL 레벨에서 처리가 가능

→ 운영체제, 하드웨어 제약 없이 사용 가능

테이블 압축 알고리즘은 Zlib 사용

단점

  • 버퍼 풀 공간 낭비
  • 쿼리 처리 성능 감소
  • 빈번한 데이터 수정 시 압축률 감소
  • CPU 사용률 증가

테이블 생성

  • 테이블이 별도의 테이블 스페이스에 있어야 가능

    • 시스템 변수 innodb_file_per_tableON 으로 설정
    SET GLOBAL innodb_file_per_table=ON;
  • 테이블 생성 시 ROW_FORMAT=COMPRESSED 옵션 추가

  • KEY_BLOCK_SIZE 옵션으로 압축 목표 크기 지정

    • 위 옵션을 쓰면 ROW_FORMAT=COMPRESSED 옵션은 자동으로 설정되므로 생략 가능

    • 1KB, 2KB, 4KB, 8KB, 16KB 로 설정 가능 (16KB 이하의 2^n KB)

      예 : 페이지 크기 (innodb_page_size) 가 16KB일 때 → 4KB, 8KB 가능(권장?)

      • 1KB, 2KB → 너무 작은 크기 어려움
      • 16KB → 원본 페이지 크기(16KB) 보다 작아야 함 (압축 의미 X)
    • 페이지 크기 32KB, 64KB 에서는 지원하지 않음

책에서는 2n으로 설명하고 있어 6KB도 가능한 줄 알았으나 설정 가능한 페이지 크기가 제한되어 있음

테이블 생성 쿼리

CREATE TABLE t2 (
    id INT PRIMARY KEY,
    data VARCHAR(100)
) ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;

-- KEY_BLOCK_SIZE를 명시할 때 ROW_FORMAT=COMPRESSED 생략 가능
CREATE TABLE t2 (
    id INT PRIMARY KEY,
    data VARCHAR(100)
) KEY_BLOCK_SIZE=8;

압축 과정

원본 데이터를 목표 크기까지 압축 시도

압축 실패 시 페이지 스플릿 후 다시 압축 수행 (목표 크기 보다 작거나 같을 때까지 반복)

테이블 압축 작동 방식
테이블 압축 작동 방식

16KB 데이터 페이지를 8KB의 KEY_BLOCK_SIZE 로 압축하는 과정

Case 1 : 압축된 결과가 8KB 이하이면 그대로 저장 (압축 완료)

Case 2 : 압축된 결과가 8KB 초과이면 원본 페이지를 스플릿(Split)해서 2개의 페이지에 8KB씩 저장

스플릿 된 페이지 각각을 다시 압축 수행

KEY_BLOCK_SIZE 설정

값은 1KB, 2KB, 4KB, 8KB, 16KB 로 설정 가능 (16KB 이하의 2^n KB)

그러나 너무 작은 값일 경우 재구성(Split) 작업을 위한 오버헤드 발생 가능

4KB, 8KB로 테이블을 생성해서 테스트(더미) 데이터를 저장해 보고 적절히 판단

책에서는 실패율은 3~5%미만 유지 권장


  1. 인덱스 별로 압축 실행 성공/실패 횟수 기록을 위해 innodb_cmp_per_index_enabled 시스템 변수를 ON 설정
SET GLOBAL innodb_cmp_per_index_enabled=ON;
  1. 인덱스 별 압축 성공/실패 이력 조회
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;

버퍼 풀 관리

  • 압축된 상태와 압축 해제된 상태 두 가지 버전을 관리
  • InnoDB는 LRU 리스트(압축된 상태)와 Unzip_LRU 리스트(압축 해제된 상태)를 별도 관리
    • LRU 리스트 : 압축된, 되지 않은 페이지를 모두 가질 수 있음

    • Unzip_LRU 리스트 : 압축이 적용되지 않은 테이블은 X → 압축 해제한 데이터 페이지 목록 관리

      → 버퍼 풀 공간을 이중으로 사용 ⇒ 메모리 낭비

    • 이러한 단점을 보완하기 위해 Unzip_LRU 리스트를 별도로 관리

      → 요청에 따라 MySQL이 적절히(Adaptive) 수행

주요 설정 변수

  • innodb_compression_level : 압축률 설정 (0~9) (기본값 : 6)
  • innodb_compression_failure_threshold_pct : 압축 실패율 임계값
  • innodb_compression_pad_pct_max : 최대 패딩(빈 공간, Padding) 공간 비율 (%로 설정)
    • 시스템 설정값 이상 불가
  • innodb_log_compressed_pages : 압축된 페이지의 로깅 여부 (기본값 : ON)

결론

  • 성능이 중요한 경우 사용 X
  • 요즘은 서버 자원이 싸고 좋아져서 (예 : HDD → SSD)

용량이 너무 크지 않는 이상 사용하지 않을 것 같다.

  • 몇 년 이상 경과되었으나 삭제하기는 애매한 경우(ex. 로그, 주문기록 등?)에서는 사용할 것 같다.

참고자료

  • MySQL 8.0 Reference Manual (링크)
  • Real MySQL 8.0 1권 (백은빈, 이성욱)
@lim
기억은 기록기록