forDevLife
InnoDB 스토리지 엔진 아키텍처 2 본문
다음 내용에 이어서 작성된다.
5. 언두 로그(Undo Log)
InnoDB는 트랜잭션과 격리 수준을 보장하기 위해 DML로 변경되기 이전 버전의 데이터를 별도로 백업한다. 이렇게 백업된 데이터를 언두 로그라고 하며 이를 아래와 같은 방식으로 활용할 수 있다.
- 트랜잭션 보장 : 롤백 시 언두 로그에 백업된 데이터로 복구한다.
- 격리 수준 보장 : 트랜잭션 격리 수준에 맞게 언두 로그에 백업한 데이터를 읽어서 반환한다.
기존 버전(8.0 이전) 문제점
- 한번 생성된 언두 로그의 공간은 서버를 새로 구축하지 않는 한 줄일 수 없었다. 따라서 백업할 때마다 커져버린 언두 로그 공간도 함께 복사해야하는 비효율성이 발생하였다.
- 기존에는 언두 로그는 모두 시스템 테이블 스페이스(ibdata.ibd)에 저장됐다. MySQL 서버가 초기화될 때 생성되기 때문에 확장의 한계가 있었다.
해결(8.0 이후)
- 불필요하거나 과도하게 할당된 공간을 운영체제로 반납할 수 있게 되었다. (Undo tablespace truncate)
-> 퍼지 스레드(Purge Thread)가 주기적으로 불필요해진 언두 로그를 삭제하고, 불필요한 공간을 반납한다. - 언두 테이블스페이스라는 구조로 저장하며, 시스템 테이블 스페이스 외부의 별도 로그 파일에 기록되도록 개선되었다. 또한 새로운 언두 테이블스페이스를 동적으로 추가하고 삭제할 수 있게 되었다.
6. 체인지 버퍼
레코드가 INSERT, UPDATE 될 때 해당 테이블에 포함된 인덱스를 업데이트하는 작업도 필요하다. 변경해야 할 인덱스 페이지가 버퍼 풀에 있다면 바로 업데이트를 수행하지만 디스크에서 읽어와서 업데이트해야 한다면 이를 즉시 실행하지 않고 임시 공간에 저장해두는데, 이 공간을 체인지 버퍼(Change Buffer)라고 한다.
특징은 다음과 같다.
- 결과 전달 전 반드시 중복 여부를 체크해야 하는 유니크 인덱스는 체인지 버퍼를 사용할 수 없다.
- 체인지 버퍼에 임시로 저장된 인덱스 레코드 조각은 이후 백그라운드 스레드(체인지 버퍼 머지 스레드)에 의해 병합된다.
- 시스템 변수를 통해 작업의 종류별(insert, deletes, purges)로 체인지 버퍼를 활성화할 수 있다.
7. 리두 로그(Redo Log) 및 로그 버퍼
리두 로그
MySQL 서버를 포함한 대부분 데이터베이스 서버는 데이터 변경 내용을 로그로 먼저 기록한다. 이를 WAL(Write Ahead Log) 로그라고도 하며, 데이터를 디스크에 기록하기 전에 먼저 기록되는 로그라는 의미이다. 이를 먼저 작성하는 이유는 다음과 같다.
- 데이터를 쓰기 위해서는 디스크의 랜덤 엑세스가 필요하며, 이 작업은 큰 비용이 든다.
- 이러한 성능 저하를 막기 위해 쓰기 비용이 낮은 자료구조를 가진 자료구조가 필요하다. 이게 리두로그이며, 성능을 위해 리두 로그를 버퍼링할 수 있는 로그 버퍼와 같은 자료구조도 가지고 있다.
- 비정상 종료가 발생하면 리두 로그의 내용을 통해 데이터 파일을 다시 서버 종료 직전 상태로 복구할 수 있다.
데이터베이스 서버에서 리두 로그는 트랜잭션이 커밋되면 즉시 디스크로 기록되도록 시스템 변수를 설정하는 것이 권장된다. 왜냐하면 서버가 비정상적으로 종료되었을 때 직전까지의 트랜잭션 커밋 내용이 기록되어야 하며, 이를 통해 장애 직전 시점까지 복구가 가능해지기 때문이다. 하지만 이 작업은 많은 부하를 유발하기 때문에 어느 주기로 동기화할지 결정하는 시스템 변수를 제공한다.
innodb_flush_log_at_trx_commit
0 : 1초에 한 번씩 리두 로그를 디스크로 기록하고 동기화한다. 따라서 1초동안의 기록이 (커밋이 되었다고 해도) 손실될 수 있다.
1 : 매번 커밋될 때마다 바로 디스크로 기록하고 동기화한다. 손실이 없다.
2 : 매번 커밋될 때마다 메모리 버퍼로 기록이 되며 실질적인 동기화(메모리 -> 디스크)는 1초에 한 번 실행된다. 따라서 운영체제가 비정상적으로 종료되면 1초동안의 기록이 손실될 수 있다.
로그 버퍼
사용량이 매우 많은(변경 작업) DBMS 서버의 경우 리두 로그의 기록 작업이 큰 부하를 줄 수 있다. 이러한 작업 역시 버퍼링을 통해 개선할 수 있으며 이 때 사용되는 공간이 로그 버퍼이다.
로그 버퍼의 크기는 기본 16MB이며, BLOB나 TEXT와 같이 큰 데이터를 자주 변경하는 경우에는 더 크게 설정하는 것이 좋다.
리두 로그 아카이빙
백업 과정(MySQL 엔터프라이즈 백업, Xtrabackup tool)에서 데이터 파일을 복사하는 동안 InnoDB 스토리지 엔진의 리두 로그에 쌓인 내용을 계속 추적하면서 새로 추가된 리두 로그 엔트리를 복사한다.
만약 백업 과정과 동시에 데이터 변경이 많이 발생하게 되면 리두 로그가 덮어쓰여질 우려가 있다. 이렇게 된다면 백업되지 않은 리두 로그가 존재하기 때문에 백업이 실패하게 된다. 이를 해결하고자 리두 로그 아카이빙 기능을 활용할 수 있다. 이는 리두 로그 파일에 로그 엔트리가 추가될 때 설정한 아카이브 파일에 변경 사항이 함께 기록하는 방식이며, 따라서 로그가 덮어 씌워져도 문제 없다.
사용법은 다음과 같다.
1. 아카이빙된 리두 로그가 저장될 디렉터리를 시스템 변수에 설정한다.
linux> mkdir /var/log/mysql_redo_archive
-- 유저만 접근할 수 있도록 chmod 설정
mysql> SET GLOBAL innodb_redo_log_archive_dirs='backup:/var/log/mysql_redo_archive';
2. innodb_redo_log_archive_start를 실행한다. 이는 UDF(User Defined Function : 사용자 정의함수)이다.
mysql> DO innodb_redo_log_archive_start('backup', '20220517');
- 첫 번재 파라미터는 아카이빙할 디렉터리에 대한 레이블이다.
- 두 번째 파라미터는 서브디렉터리 이름이다(생략 가능)
3. 다음 명령으로 종료한다.
mysql> DO innodb_redo_log_archive_stop();
8. 어댑티브 해시 인덱스
InnoDB 스토리지 엔진에서 사용자가 자주 요청하는 데이터에 대해 자동으로 생성하는 인덱스이며, innodb_adaptive_hash_index 시스템 변수를 통해 기능을 활성화 / 비활성화할 수 있다.
B-Tree의 인덱스에서 특정 값을 찾는 속도는 상대적이며, 경우에 따라서는 성능 저하가 발생할 수 있고 이러한 검색 시간을 줄여주기 위해 도입된 기능이다. 자주 읽히는 데이터 페이지의 키 값을 이용해 해시 인덱스를 만들고, 필요할 때마다 해당 인덱스를 검색해서 레코드가 저장된 데이터 페이지를 즉시 찾아갈 수 있다.
해시 인덱스는 다음과 같은 key-value 쌍으로 관리된다.
Key | Value |
B-Tree 인덱스의 고유 번호(Id), B-Tree 인덱스의 실제 키 값 조합 | InnoDB 버퍼 풀에 로딩된 데이터 페이지의 메모리 주소 |
key에서 B-Tree 인덱스의 고유 번호가 필요한 이유는, 모든 B-Tree 인덱스에 대한 어댑티브 해시 인덱스가 하나의 해시 인덱스에 저장되기 때문이다. 따라서 특정 키 값이 어떤 인덱스에 속한건지도 구분해야 하기 때문에 인덱스의 고유 번호도 사용된다.
핵심은 Value이며, 버퍼 풀에 있는 페이지 주소를 읽어온다. 즉 B-Tree 인덱스를 활용하여 디스크에서 레코드를 읽어오는 것보다 버퍼 풀에서 바로 찾아오는게 I/O가 없기 때문에 당연히 훨씬 빠르다.
따라서 다음과 같이 정리해볼 수 있다.
- 어댑티브 해시 인덱스는 버퍼 풀에 올려진 데이터 페이지에 대해서만 관리된다. 따라서 매우 빠른 접근이 가능하다.
- 버퍼 풀에서 해당 페이지가 사라지면 어댑티브 해시 인덱스에서도 해당 페이지 정보가 사라진다.
아래 경우에 따라 활성화 / 비활성화 여부를 선택해서 성능을 최적화할 수 있다.
도움 되는 경우
- 디스크의 데이터가 InnoDB 버퍼 풀 크기와 비슷한 경우(디스크 읽기가 많지 않은 경우)
- 동등 조건 검색(동등 비교와 IN 연산자)이 많은 경우
- 쿼리가 데이터 중 일부 데이터에만 집중되는 경우
도움되지 않는 경우
- 디스크 읽기가 많은 경우
- 특정 패턴의 쿼리가 많은 경우(조인, LIKE 패턴 검색)
- 매우 큰 데이터를 가진 테이블의 레코드를 폭넓게 읽는 경우
'Database' 카테고리의 다른 글
인덱스 (0) | 2022.05.31 |
---|---|
트랜잭션과 잠금 (0) | 2022.05.24 |
InnoDB 버퍼 풀과 주변 기능 (0) | 2022.05.17 |
InnoDB 스토리지 엔진 아키텍처 1 (0) | 2022.05.12 |
스레드 캐시, Connection Pool, Thread Pool (0) | 2022.05.10 |