MySQL 서버에서 디스크에 저장된 데이터 파일의 크기는 쿼리의 처리 성능과도 직결되지만 백업 및 복구 시간과도 밀접하게 연결된다.
디스크의 데이터 파일이 크면 클수록 쿼리 처리를 하기 위해서 더 많은 데이터 페이지를 InnoDB 버퍼 풀로 읽어야 할 수도 있고, 새로운 페이지가 버퍼 풀로 적재되기 때문에 그만큼 더티 페이지가 더 자주 디스크로 기록돼야 한다. 그리고 데이터 파일이 크면 클수록 백업 시간이 오래 걸리며, 복구하는 데도 그만큼의 시간이 걸린다. 이러한 문제점을 해결하기 위해 데이터 압축 기능을 많은 DBMS에서 제공한다.
페이지 압축
- "Transparent Page Compression"이라고도 불림
- 위처럼 불리는 이유는 MySQL 서버가 디스크에 저장하는 시점에 데이터 페이지가 압축되어 저장되고, 반대로 MySQL 서버가 디스크에서 데이터 페이지를 읽어올 때 압축이 해제되기 때문
- 즉, 버퍼 풀에 데이터 페이지가 한 번 적재되면 InnoDB 스토리지 엔진은 압축이 해제된 상태로만 데이터 페이지를 관리한다.
- MySQL 서버의 내부 코드에서는 압축 여부와 관계없이 "투명(Tranparent)"하게 작동한다.
펀치 홀
- 16KB 데이터 페이지를 압축한 결과가 용량이 얼마나 될지 예측이 불가능한데 적어도 하나의 테이블은 동일한 크기의 페이지(블록)으로 통일돼야 한다는 것.
- 그래서 페이지 압축 기능은 운영체제별로 특정 버전의 파일 시스템에서만 지원되는 '펀치 홀(Punch hole)'이라는 기능을 사용.
왜 하나의 테이블은 동일한 크기의 페이지(블록)으로 통일되어야 하는가?
- 테이블의 크기가 일정하다면, 읽거나 쓸 때 발생하는 I/O 작업이 일관되게 이루어진다.
- 메모리에 적재되는 페이지의 크기를 효과적으로 관리할 수 있다.
- 테이블의 크기가 일정하면 페이지 분할과 병합이 발생할 가능성이 줄어든다.
- 테이블에 대한 공간 할당 및 해제가 간편하고 예측 가능하며, 테이블 관리에 필요한 작업들이 단순화된다.
페이지 압축이 작동하는 방식 (운영체제의 블록 사이즈가 512바이트 인 경우)
- 16KB 페이지를 압축 (압축 결과를 7KB로 가정)
- MySQL 서버는 디스크에 압축된 결과 7KB를 기록 (이때 MySQL 서버는 압축 데이터 7KB에 9KB의 빈 데이터를 기록)
- 디스크에 데이터를 기록한 후, 7KB 이후의 공간 9KB에 대해 펀치 홀(Punch-hole)을 생성
- 파일 시스템은 7KB만 남기고 나머지 디스크의 9KB 공간은 다시 운영체제로 반납
다음과 같은 과정을 거친 그림이 아래와 같다.
MySQL 서버의 페이지 압축이 가진 문제
- 펀치 홀 기능은 운영체제뿐만 아니라 하드웨어 자체에서도 해당 기능을 지원해야 사용 가능하다는 점
- 아직 파일 시스템 관련 명령어(유틸리티)가 펀치 홀을 지원하지 못한다는 것
이러한 이유 외의 다른 이유들로 인해 페이지 압축은 많이 사용되지 않는 상태다.
테이블 압축
운영체제나 하드웨어에 대한 제약 없이 사용할 수 있기 때문에 일반적으로 더 활용도가 높은 편
- 장점 : 디스크의 데이터 파일 크기를 줄일 수 있다.
- 단점
- 버퍼 풀 공간 활용률이 낮음
- 쿼리 처리 성능이 낮음
- 빈번한 데이터 변경 시 압축률이 떨어짐
압축 테이블 생성
- 테이블 압축을 사용하기 위한 전제 조건으로 압축을 사용하려는 테이블이 별도의 테이블 스페이스를 사용해야 한다.
- innodb_file_per_table 시스템 변수가 ON으로 설정된 상태에서 테이블이 생성되어야 한다.
- 테이블 압축을 사용하는 테이블은 테이블 생성 시 ROW_FORMAT=COMPRESSED 옵션을 명시해야 한다.
- KEY_BLOCK_SIZE 옵션을 이용해 압축된 페이지의 목표 크기를 명시하는데, 2n(n 은 2 이상)으로만 설정할 수 있다.
- 페이지 크기가 32KB 또는 64KB인 경우에는 테이블 압축을 적용할 수 없다.
InnoDB 스토리지 엔진이 압축을 적용하는 방법
- 16KB의 데이터 페이지를 압축
- 압축된 결과가 8KB 이하이면 그대로 디스크에 저장(압축 완료)
- 압축된 결과가 8KB를 초과하면 원본 페이지를 스플릿(split)해서 2개의 페이지에 8KB씩 저장
- 나뉜 페이지 각각에 대해 1번 단계를 반복 실행
테이블 압축 방식에서 가장 중요한 것은 원본 데이터 페이지의 압축 결과가 목표 크기(KEY_BLOCK_SIZE)보다 작거나 같을 때까지 반복해서 페이지를 스플릿하는 것.
KEY_BLOCK_SIZE 결정
- 테이블 압축에서 가장 중요한 부분은 압축된 결과가 어느 정도가 될지를 예측해서 KEY_BLOCK_SIZE를 결정하는 것
- 따라서, 테이블 압축을 적용하기 전에 KEY_BLOCK_SIZE를 4KB 또는 8KB로 테이블을 생성해서 최소한 10개의 샘플 데이터를 저장해보고 적절한지 판단하는 것이 좋다.
- 일반적으로 압축 실패율은 3~5% 미만으로 유지할 수 있게 KEY_BLOCK_SIZE를 선택하는 것이 좋다.
압축된 페이지의 버퍼 풀 적재 및 사용
- InnoDB 스토리지 엔진은 압축된 테이블의 데이터 페이지를 버퍼 풀에 적재하면 압축된 상태와 압축이 해제된 상태 2개 버전을 관리한다.
- 디스크에서 읽은 상태 그대로의 데이터 페이지 목록을 관리하는 LRU 리스트
- 압축된 페이지들의 압축 해제 버전인 Unzip_LRU 리스트
- Unzip_LRU 리스트는 압축이 적용되지 않은 테이블의 데이터 페이지는 가지지 않는다.
문제점
- InnoDB 스토리지 엔진은 압축된 테이블에 대해서는 버퍼 풀의 공간을 이중으로 사용함으로써 메모리를 낭비하는 효과를 가짐
- 압축된 페이지에서 데이터를 읽거나 변경하기 위해서는 압축을 해제해야 한다. 이는 CPU를 많이 소모하는 작업이다.
이러한 두 가지 단점을 보완하기 위해 Unzip_LRU 리스트를 별도고 관리하고 있다가 MySQL 서버로 유입되는 요청 패턴에 따라서 적절히 다음과 같은 처리를 수행한다.
- InnoDB 버퍼 풀의 공간이 필요한 경우에는 LRU 리스트에서 원본 데이터 페이지(압축된 형태)는 유지하고, Unzip_LRU 리스트에서 압축 해제된 버전은 제거해서 버퍼 풀의 공간을 확보한다.
- 압축된 데이터 페이지가 자주 사용된느 경우에는 Unzip_LRU 리스트에 압축 해제된 페이지를 계속 유지하면서 압축 및 압축 해제 작업을 최소화한다.
- 압축된 데이터 페이지가 사용되지 않아서 LRU 리스트에서 제거되는 경우에는 Unzip_LRU 리스트에서도 함께 제거된다.
테이블 압축 관련 설정
테이블 압축을 사용할 때 연관된 시스템 변수가 몇 가지 있는데, 모두 페이지의 압축 실패율을 낮추기 위해 필요한 튜닝 포인트를 제공.
innodb_cmp_per_index_enabled | 테이블 압축이 사용된 테이블의 모든 인덱스별로 압축 성공 및 압축 실행 횟수 수집 |
innodb_compression_level | InnoDB 테이블 압축은 zlib 압축 알고리즘만 지원하며, 해당 시스템 변수를 이용해 압축률을 설정한다. 0~9의 범위를 갖고, 값이 작을수록 압축 속도는 빠르지만 저장 공간은 커진다. 기본값은 6이다. 압축 속도는 CPU 자원 소모량과 동일한 의미다. |
innodb_compression_failure_threshold_pct | 테이블 단위로 압축 실패율이 해당 값 보다 커지면 압축을 실행하기 전 원본 데이터 페이지 끝에 의도적으로 일정 크기의 빈공간을 추가된다. 빈 공간은 압축률을 높여 압축 결과가 KEY_BLOCK_SIZE보다 작어지게 만든다. 추가하는 빈 공간을 패딩이라고 한다. |
inno_compression_pad_pct_max | 패딩은 압축 실패율이 높아질수록 계족 증가하지만 해당 값 이상을 넘을 수 없다. 설정값에는 % 값을 설정하고, 전체 데이터 페이지 크기 대비 패딩 공간의 비율을 의미한다. |
'MySQL 8.0' 카테고리의 다른 글
인덱스 (2) | 2024.01.25 |
---|---|
데이터 암호화 (0) | 2024.01.17 |
트랜잭션과 잠금 (3) | 2024.01.11 |
InnoDB 스토리지 엔진 아키텍처(2) (1) | 2024.01.10 |
InnoDB 스토리지 엔진 아키텍처 (0) | 2024.01.04 |