빙응의 공부 블로그
[Real MySQL 8.0]4장-2 InnoDB 본문

📝4.2 InnoDB 스토리지 엔진 아키텍처
MySQL의 스토리지 엔진 가운데 가장 많이 사용하는 InnoDB 스토리지 엔진에 대해 살펴봅시다.
InnoDB 스토리지 엔진은 MySQL 스토리지 엔진 중 거의 유일하게 레코드 기반의 잠금을 제공하며, 그 때문에 높은 동시성 처리가 가능하고 안정적이며 성능이 뛰어납니다.

🧷4.2.1 프라이머리 키에 의한 클러스터링
InnoDB의 모든 테이블은 기본적으로 프라이머리 키를 기준을 클러스터링되어 저장됩니다.
즉, PK를 기준으로 순서대로 디스크에 저장된다는 뜻입니다. 모든 세컨더리 인덱스(클러스터링 인덱스 제외 모든 인덱스)는
레코드의 주소 대신 프라이머리 키의 값을 논리적인 주소로 사용합니다.
그렇기에 테이블에는 기본적인 기본키 인덱스가 존재하여 범위 스캔에 대해 상당히 빨리 처리될 수 있어 쿼리 실행 계획에서 프라이머리 키는 기본적으로 다른 보조 인덱스에 비해 비중이 높습니다.
🧷4.2.2 외래 키 지원
InnoDB는 외래키를 지원합니다. (MyISAM 이나 MEMORY 테이블은 지원 X)
외래 키는 데이터베이스 서버 운영의 불편함 때문에 서버용 데이터베이스에서는 생성하지 않는 경우도 자주 있는데, 그렇다 하더라도 개발 환경의 데이터베이스에서 좋은 가이드 역할을 합니다.
InnoDB에서 외래 키는 부모 테이블과 자식 테이블 모두 해당 칼럼에 인덱스 생성이 필요하고 변경 시에는 반드시 부모 테이블이나 자식 테이블에 데이터가 있는지 체크가 필요하기 대문에 데드락이 발생할 때가 많으므로 주의해야합니다.
외래 키의 단점 및 운영상의 불편함
- 데드락 위험:
- 외래 키가 있는 테이블에서 데이터 수정/삭제 시 부모와 자식 테이블 간의 참조 무결성을 확인하는 과정에서 데드락이 발생할 가능성이 있습니다.
- 성능 저하:
- 외래 키 무결성 검사를 위해 추가적인 데이터 조회 및 검증 작업이 필요하므로, 성능 저하가 발생할 수 있습니다.
- 유지보수 어려움:
- 스키마 변경(컬럼 추가/삭제, 테이블 리팩토링 등)이 있을 때 외래 키 제약 조건 때문에 복잡도가 증가합니다.
그렇기에 외래키 자체를 논리적으로만 설정하고 개발자 분들이 비즈니스 계층에서 데이터베이스의 정합성을 관리하는 방법도 사용합니다.
🧷4.2.3 MVCC(Multi Version Concurrency Control)
일반적으로 레코드 레벨의 트랜잭션을 지원하는 DBMS가 지원하는 기능입니다.
MVCC의 가장 큰 목적은 잠금을 사용하지 않는 일관된 읽기를 제공하는 데 있습니다.
잠금을 사용하지 않는다는 건 읽기에서 트랜잭션 락을 걸지 않는 것을 의미합니다.
InnoDB는 언두 로그를 이용하여 이 기능을 구현합니다.(언두 로그는 트랜잭션 롤백을 위한 과거 데이터를 저장하는 로그)
CREATE TABLE member (
m_id INT NOT NULL,
m_name VARCHAR(20) NOT NULL,
m_area VARCHAR(100) NOT NULL,
PRIMARY KEY (m_id),
INDEX ix_area(m_area)
);
INSERT INTO member VALUES (1, '홍길동', '서울');
INSERT INTO member VALUES (2, '김철수', '부산');
예시 상황
m_id | m_name | m_area |
1 | 홍길동 | 서울 |
2 | 김철수 | 부산 |
1. 트랜잭션 A 시작
- 트랜잭션 A가 데이터를 읽습니다:
- 트랜잭션 A는 m_id = 1의 데이터를 읽고, 홍길동을 반환합니다.
START TRANSACTION; SELECT * FROM member WHERE m_id = 1;
2. 트랜잭션 B 시작 (데이터 수정)
- 트랜잭션 B가 데이터를 수정합니다:
- InnoDB는 UPDATE 작업을 수행하면서 언두 로그에 홍길동을 저장하고, 메모리에서 박영희로 변경합니다. 하지만 트랜잭션 A가 완료될 때까지 트랜잭션 A는 변경된 데이터를 볼 수 없습니다.
START TRANSACTION; UPDATE member SET m_name = '박영희' WHERE m_id = 1;
3. 트랜잭션 A 계속 진행
- 트랜잭션 A가 다시 m_id = 1을 읽습니다:
- 트랜잭션 A는 홍길동을 읽습니다. 트랜잭션 A는 트랜잭션 B가 커밋되기 전의 스냅샷을 사용하고 있기 때문에, 박영희는 보지 못합니다.
SELECT * FROM member WHERE m_id = 1;
4. 트랜잭션 B 커밋
- 트랜잭션 B가 변경 사항을 커밋합니다:
- 이제 다른 트랜잭션은 m_name = '박영희'로 변경된 값을 볼 수 있습니다.
COMMIT;
5. 트랜잭션 A 종료 후 확인
- 트랜잭션 A가 종료되고, 이후에 데이터를 다시 조회합니다:
- 결과: 박영희 (트랜잭션 B에서 커밋된 최신 데이터)
SELECT * FROM member WHERE m_id = 1;
해당 결과는 MySQL 서버의 시스템 변수에 설정된 격리 수준에 따라 다릅니다. 지금 보여주는 예는 READ_UNCOMMITTED의 경우입니다.
그렇다면 해당 언두 영역은 언제 삭제될까요? 그것은 2가지입니다. 바로 롤백이 되어 언두 로그로 돌아갈 때와 이제 트랜잭션에서 해당 언두로그가 필요없어졌을 때입니다.
🧷4.2.4 잠금 없는 일관된 읽기
InnoDB 스토리지 엔진은 MVCC 기술을 이용해 잠금을 걸지 않고 읽기 작업을 수행합니다.
잠금을 걸지 않기에 InnoDB에서 읽기 작업은 다른 트랜잭션이 가지고 있는 잠금을 기다리지 않고 즉시 읽기를 실시합니다.
격리 수준에 따라 읽기 작업의 동작이 달라질 수 있지만, 대부분의 읽기 작업은 INSERT와 연결되지 않은 순수한 읽기 작업이라면, 다른 트랜잭션의 변경 작업과 관계없이 즉시 읽기를 수행합니다. 즉, 트랜잭션이 다른 데이터의 변경 작업을 완료하고 커밋되기 전이라도, 읽기 작업은 MVCC의 스냅샷을 기반으로 데이터를 읽기 때문에 읽기 작업에 영향을 주지 않습니다.
🧷4.2.5 자동 데드락 감지
InnoDB 스토리지 엔진은 내부적으로 잠금이 교착 상태에 빠지지 않았는지 체크하기 위해 잠금 대기 목록을 그래프 형태로 관리합니다.
InnoDB는 데드락 감지 스레드를 가지며 주기적으로 잠금 대기 그래프를 검사해 교착 상태에 빠진 트랜잭션을 찾아 강제 종료합니다. (이때 어떤 트랜잭션을 강제 종료할지는 언두 로그의 양으로 결정합니다. 작은 것을 먼저 롤백하며 이유는 영향 최소화입니다.)
사실 일반적인 서비스에서는 이러한 행동이 부담스럽지 않으나 구글같은 매우 높은 동시성을 요구하는 곳에 경우 데드락 감지 스레드는 매우 부담될 수 있기에 해당 설정을 끈다고 합니다.
🧷4.2.7 InnoDB 버퍼 풀
InnoDB 버퍼 풀은 InnoDB 스토리지 엔진에서 매우 중요한 역할을 하는 메모리 영역입니다. 버퍼 풀은 디스크에 저장된 데이터 파일과 인덱스 정보를 메모리에 캐시하는 공간으로, 데이터베이스 성능을 크게 향상시키는 핵심 요소입니다.
📌4.2.7.2 버퍼 풀의 크기
InnoDB 스토리지 엔진에서 버퍼 풀(Buffer Pool) 은 메모리 내에서 데이터 페이지를 저장하는 핵심 메모리 공간입니다. 이 공간은 페이지 단위로 나누어져 있으며, 각 페이지는 데이터 파일이나 인덱스 페이지 등의 데이터를 저장하고 있습니다. InnoDB는 이 버퍼 풀을 효과적으로 관리하고 최적화하기 위해 여러 자료 구조를 사용합니다. 주요 자료 구조는 LRU (Least Recently Used) 리스트, 플러시 리스트(Flush List), 그리고 프리 리스트(Free List)입니다.
1. LRU (Least Recently Used) 리스트
- LRU 리스트는 가장 최근에 사용된 페이지를 앞에, 가장 오래 전에 사용된 페이지를 뒤에 배치하는 캐시 교체 알고리즘입니다.
- LRU 리스트에서 가장 중요한 역할은 데이터 페이지를 버퍼 풀에서 효율적으로 교체하는 것입니다. InnoDB는 데이터를 읽거나 수정할 때, 해당 페이지를 LRU 리스트의 앞쪽으로 이동시켜 최근에 사용된 페이지는 더 오래 유지될 수 있도록 합니다.
- 만약 버퍼 풀이 꽉 찬 상태에서 새로운 페이지를 읽어와야 할 경우, 가장 오래된 페이지(LRU 리스트의 끝 부분에 위치한 페이지)를 디스크로 플러시(Flush)하여 공간을 비우고, 새로운 페이지를 버퍼 풀에 할당합니다.
2. 플러시 리스트 (Flush List)
- 플러시 리스트는 버퍼 풀에 있는 더티 페이지(Dirty Pages)를 관리하는 자료 구조입니다. 더티 페이지는 메모리에서 수정된 데이터로, 아직 디스크에 반영되지 않은 상태입니다.
- 버퍼 풀에 수정된 데이터를 읽거나 쓰는 작업이 이루어질 때, 해당 페이지는 더티 페이지로 표시됩니다. 이러한 더티 페이지는 주기적으로 디스크에 플러시되어야 하며, 플러시 리스트는 이 과정을 추적하는 데 사용됩니다.
- 플러시 리스트에 있는 페이지는 디스크로 플러시되기 전까지 메모리에 유지됩니다. 이를 통해 쓰기 작업을 지연시키고, 효율적으로 처리할 수 있습니다.
3. 프리 리스트 (Free List)
- 프리 리스트는 버퍼 풀에서 사용되지 않는 페이지를 관리하는 자료 구조입니다. 즉, 빈 페이지들이 여기에 저장됩니다.
- 이 프리 리스트는 버퍼 풀에 새로운 페이지를 읽을 때 해당 페이지가 비어있는 공간일 경우 이 공간을 재활용하기 위해 사용됩니다. 새로운 데이터를 읽어들일 때, 프리 리스트에서 빈 공간을 찾아 해당 데이터를 할당하고, 그 페이지를 프리 리스트에서 제거합니다.
- 프리 리스트는 메모리 공간의 재활용을 관리하며, 버퍼 풀을 효과적으로 운영하는 데 중요한 역할을 합니다.
버퍼 풀의 동작 과정
- 데이터 읽기: InnoDB는 데이터를 읽을 때 먼저 버퍼 풀에서 해당 페이지를 찾고, 있으면 바로 메모리에서 데이터를 반환합니다. 만약 없으면 디스크에서 해당 페이지를 읽어와 버퍼 풀에 저장하고, 페이지가 더티 페이지로 변경되면 플러시 리스트에 추가됩니다.
- 데이터 수정: 데이터를 수정하면 해당 페이지는 더티 페이지로 표시되고, LRU 리스트에서 위치가 변경됩니다.
- 공간 부족 시 페이지 교체: 버퍼 풀이 꽉 차면 LRU 리스트를 따라 가장 오래된 페이지를 디스크로 플러시하고, 그 공간을 새로운 페이지로 대체합니다.
- 디스크 플러시: 플러시 리스트에서 더티 페이지는 디스크로 플러시되며, 페이지가 디스크에 반영되면 더 이상 더티 페이지로 간주되지 않습니다
📌4.2.7.2 버퍼 풀과 리두 로그
InnoDB에서 버퍼 풀(Buffer Pool)과 리두 로그(Redo Log)는 매우 중요한 관계를 맺고 있으며, 함께 작동하여 데이터베이스의 성능과 안정성을 향상시키는데 핵심적인 역할을 합니다.

버퍼 풀과 리두 로그의 관계
- 버퍼 풀은 데이터와 인덱스 정보를 메모리에서 캐시하는 영역으로, 디스크 I/O를 최소화하여 쿼리 성능을 향상시키는 중요한 역할을 합니다.
- 리두 로그는 트랜잭션 로그의 일종으로, 데이터베이스에 대한 변경 사항을 기록합니다. 이 로그는 트랜잭션이 커밋되기 전에 데이터의 변경 사항을 디스크에 안전하게 기록하는 데 사용됩니다. 리두 로그는 트랜잭션 복구 및 장애 복구에 중요한 역할을 합니다.
버퍼 풀의 기능과 리두 로그의 역할
- 데이터 캐시:
- 버퍼 풀의 주요 기능 중 하나는 디스크의 데이터를 메모리에 캐시하는 것입니다. 디스크에서 데이터를 읽을 때마다 I/O 작업이 발생하는데, 이를 최소화하기 위해 자주 사용하는 데이터를 메모리에 캐시해두는 것입니다.
- 버퍼 풀의 크기가 커지면 더 많은 데이터와 인덱스를 메모리에서 직접 처리할 수 있어, 디스크 I/O가 줄어들고 쿼리 성능이 향상됩니다.
- 쓰기 버퍼링:
- 버퍼 풀은 쓰기 작업을 지연시키는 역할도 합니다. 예를 들어, 디스크에 직접 쓰는 작업은 느리기 때문에, 쓰기 작업을 먼저 버퍼 풀에서 처리하고, 일정 시점에 일괄적으로 디스크에 기록합니다.
- 이때 리두 로그가 중요한 역할을 합니다. 버퍼 풀에 저장된 데이터는 즉시 디스크에 반영되지 않고 리두 로그에 변경 로그만 먼저 기록됩니다. 리두 로그는 변경된 데이터가 디스크에 제대로 기록되지 않더라도, 트랜잭션을 복구할 수 있도록 보장합니다.
- 쓰기 버퍼링과 리두 로그의 상호작용:
- 데이터가 버퍼 풀에 먼저 기록되고, 디스크에 실제로 반영되기 전에 해당 변경 사항이 리두 로그에 기록됩니다. 이 방식은 쓰기 버퍼링을 가능하게 하고, 디스크 I/O를 효율적으로 관리합니다.
- 변경된 데이터가 버퍼 풀에 저장된 뒤, 주기적으로 플러시(Flush)되어 디스크에 반영됩니다. 리두 로그는 이 과정에서 트랜잭션의 안전성을 보장하며, 서버에 장애가 발생해도 리두 로그에 기록된 데이터를 이용하여 트랜잭션을 복구할 수 있습니다.
버퍼 풀 크기와 성능
- 버퍼 풀 크기는 성능에 중요한 영향을 미칩니다. 메모리가 충분히 제공된다면 버퍼 풀의 크기를 크게 설정하여 더 많은 데이터를 메모리에 캐시할 수 있습니다. 이로 인해 디스크 I/O가 줄어들고, 쿼리 성능이 향상됩니다.
- 그러나, 버퍼 풀 크기를 단순히 늘리는 것만으로는 쓰기 성능 향상에 직접적인 도움이 되지 않습니다. 쓰기 성능을 향상시키려면 쓰기 버퍼링이 필요하며, 리두 로그의 사용이 필수적입니다.
쓰기 버퍼링을 향상시키는 방법
- 쓰기 성능을 최적화하려면 쓰기 버퍼링과 관련된 다른 설정을 고려해야 합니다:
- log_buffer_size: 리두 로그를 메모리에서 처리할 수 있는 크기를 설정합니다. 이 값이 충분히 크면, 로그가 디스크에 기록되기 전에 메모리에서 버퍼링할 수 있어 성능이 향상됩니다.
- innodb_flush_log_at_trx_commit: 트랜잭션 커밋 시 리두 로그를 어떻게 플러시할지를 결정합니다. 1로 설정되면 각 트랜잭션 커밋 시마다 리두 로그가 즉시 디스크에 플러시되며, 성능은 다소 떨어지지만 데이터 안정성이 보장됩니다. 2로 설정되면 성능이 향상되지만, 시스템 크래시 시 일부 로그 손실이 발생할 수 있습니다.
- innodb_flush_method: 플러시 방법을 정의하는 설정으로, O_DIRECT를 사용하면 직접 I/O를 수행하여 버퍼 풀을 최적화할 수 있습니다.
📌4.2.7.4 버퍼 풀 플러시
MySQL 5.6 버전까지는 InnoDB 스토리지 더티 페이지 플러시 기능이 그다지 좋지 않았다.
MySQL 5.7 이러한 현상이 개선되었는데 이는 기록되자 않은 더티 페이지들을 성능상의 악영향 없이 디스크에 동기화하기 위해 2가지 플러시 기능을 실행시키기 때문이다.
- 1. LRU 리스트 플러시
- LRU(Least Recently Used) 리스트는 버퍼 풀에서 가장 최근에 사용된 데이터 페이지부터 가장 오랫동안 사용되지 않은 데이터 페이지까지 페이지를 관리하는 자료구조입니다.
- LRU 리스트 플러시는 버퍼 풀에서 가장 오래된 페이지(사용되지 않은 페이지)를 디스크에 플러시하는 방식입니다. 즉, 가장 오래된 페이지부터 디스크로 기록하여 메모리 공간을 확보하는 것입니다.
- LRU 리스트 플러시는 버퍼 풀에 공간을 확보하고, 오래된 데이터를 디스크에 기록하여 최신 데이터를 메모리에 더 많이 할당할 수 있게 도와줍니다.
- 메모리 최적화: 자주 사용되지 않는 데이터부터 플러시하여 메모리를 효율적으로 사용합니다.
- 디스크 I/O 부하 분산: LRU 리스트 플러시는 데이터의 변경 빈도나 중요도에 관계없이 단순히 오래된 데이터를 플러시하므로 디스크 I/O 부하를 분산시킬 수 있습니다.
- 성능 향상: 오래된 데이터를 먼저 디스크에 기록함으로써 최신 데이터가 메모리에 적절히 유지되고, 성능이 향상됩니다.
- 플러시 리스트(Flush List)는 InnoDB에서 디스크에 플러시해야 할 더티 페이지들을 관리하는 리스트입니다. 이 리스트에는 디스크에 기록되지 않은 더티 페이지들이 저장됩니다.
- 플러시 리스트 플러시는 더티 페이지를 디스크로 플러시하는 작업으로, 버퍼 풀 내의 페이지 중 디스크에 기록되지 않은 페이지들만 플러시합니다. 이 작업은 버퍼 풀의 데이터가 메모리와 디스크 간에 동기화되지 않은 상태를 해소하기 위해 중요합니다.
- 정기적 동기화: 플러시 리스트 플러시는 버퍼 풀의 상태가 디스크와 일관되도록 유지하는 데 사용됩니다. 변경된 페이지들이 디스크에 기록되지 않으면 데이터의 일관성에 문제가 생길 수 있기 때문에, 이를 해결하기 위한 과정입니다.
- 쓰기 최적화: 데이터베이스에 변경이 발생하면 먼저 메모리에 기록된 후, 일정 주기로 디스크에 기록됩니다. 이 과정에서 플러시 리스트 플러시가 이루어집니다.
- 안정성: 트랜잭션 롤백 시에도 롤백 가능한 변경 사항이 플러시 리스트에 기록되므로, 데이터베이스의 안정성을 유지할 수 있습니다.
📌4.2.7.5 버퍼 풀 상태 백업 및 복구
스프링으로 프로젝트를 하다보면 서비스 시작 시에 쿼리 처리 성능이 평상시보다 매우 안좋은 것을 알 수 있다.
이것은 디스크의 데이터가 버퍼풀에 적재하기 때문인다. 이렇게 적재가 끝난 상태를 워밍업(Warming Up)이라고 표현한다.
MySQL 6.7 버전부터 버퍼 풀 덤프 및 적재 기능을 도입하여 버퍼풀을 복구할 수 있게 되었다.
이로인해 워밍업 시간 단축 및 성능 저하에 대응할 수 있게 되었다.
🧷4.2.8 Double Write Buffer
InnoDB의 리두 로그는 공간 낭비를 막기 위해 페이지의 변경된 내용만 기록합니다. 이로 인해 InnoDB의 스토리지 엔진에서 더티 페이지를 디스크 파일로 플러시할 때 일부분만 기록될 수 있습니다. 이러한 현상은 하드웨어 오작동이나 시스템 비정상 종료 시에 발생할 수 있다.
이를 막기 위해 Double Write 기법을 이용합니다.

Double Write Buffer는 InnoDB 스토리지 엔진에서 데이터 무결성을 보호하기 위한 메커니즘입니다. InnoDB에서는 데이터 변경을 디스크에 직접 쓰기 전에, 두 번에 걸쳐 데이터를 기록하는 방식을 사용하여 데이터 손상이나 손실을 방지합니다. 이 메커니즘은 주로 서버 장애나 비정상 종료가 발생했을 때 데이터베이스의 무결성을 유지하는 데 중요한 역할을 합니다.
작동 원리
- 첫 번째 쓰기:
- 데이터가 변경되면 Double Write Buffer에 먼저 기록됩니다. 이는 InnoDB의 버퍼 풀 내에서 이루어지며, 실제 디스크에 기록되기 전에 메모리에서 일시적으로 저장됩니다.
- 두 번째 쓰기:
- 이후 InnoDB는 이 데이터를 실제 디스크의 데이터 파일에 두 번 쓰게 됩니다. 즉, 첫 번째는 Double Write Buffer로 기록되고, 두 번째는 데이터 파일로 기록됩니다.
- 장애 발생 시
- InnoDB는 재시작 될 때 DoubleWrite 버퍼의 내용과 데이터 파일의 페이지들을 모두 비교하여 다른 내용을 담고 있는 페이지가 있다면 DoubleWrite 버퍼의 내용을 데이터 파일의 페이지로 복사합니다.
🧷4.2.9 Undo Log
InnoDB 스토리지 엔진은 트랜잭션과 격리 수준을 보장하기 위해 DML로 변경 되기 이전 버전의 데이터를 언두 로그에 백업한다.
언두 로그는 어떨 때 사용할까?
- 트랜잭션 보장
- 트랜잭션이 롤백되면 트랜잭션 도중 변경 데이터를 기존 데이터로 복구하는데 이 과정에서 언두 로그에 있는 데이터를 이용한다.
- 격리 수준 보장
- 특정 커넥션에서 데이터를 변경하는 도중 데이터를 조회하면 트랜잭션 격리 수준에 맞게 변경 레코드를 읽지 않고 언두 로그 데이터를 읽는다.
📌4.2.9.2 언두 테이블스페이스 관리
언두 로그가 저장되는 공간을 언두 테이블스페이스라고 한다.
MySQL 5.6 버전 이전에는 언두 로그가 모두 시스템 테이블스페이스에 저장되었지만, 시스템 테이블스페이스의 언두 로그는 서버가 초기화될 때 생성되기에 확장에 문제가 있었다. 그래서 현재는 항상 별도의 로그 파일에 저장된다.
하나의 언두 테이블 스페이스는 1개 이상 128개 이하의 롤백 세그먼트를 가지며 롤백 세그먼트는 1개 이상의 언두 슬룻을 가진다.
하나의 롤백 세그먼트는 InnoDB의 페이지 크기를 16바이트로 나눈 값의 개수만큼의 언두 슬룻을 가진다.
(롤백 세그먼트는 트랜잭션 변경사항을 관리하는 단위이다)
언두 테이블스페이스 공간을 필요한 만큼만 남기고 불필요하거나 과도하게 할당된 공간을 운영체제에 반납하는 것을 Undo tablespace truncate라고 한다. 언두 테이블스페이스의 불필요한 공간을 잘라내는 방법은 수동과 자동 두 가지 방법이 있다.
자동 및 수동 방법:
- 자동 트렁케이트: InnoDB는 자동으로 불필요한 공간을 정리할 수 있는 메커니즘을 제공합니다. 이 경우, InnoDB는 사용하지 않는 공간을 감지하고 이를 트렁케이트하여 운영체제에 반납합니다.
- 수동 트렁케이트: 수동으로 언두 테이블스페이스를 축소하고 불필요한 공간을 재정렬하는 방법입니다. 이는 OPTIMIZE TABLE 명령어를 사용하거나, 언두 테이블스페이스를 다시 생성하는 방법으로 수행할 수 있습니다.
🧷4.2.10 체인지 버퍼
RDBMS에서 레코드가 변경될 때, 데이터 파일을 변경하는 작업뿐 아니라 해당 테이블에 포함된 인덱스를 업데이트해야 합니다.
그런데 인덱스를 업데이트하는 작업은 랜덤하게 디스크를 읽는 작업이 필요하므로, 테이블에 인덱스가 많다면 이 작업은 상당히 많은 자원을 소모합니다. 이를 해결하기 위해, InnoDB는 변경해야 할 인덱스 페이지가 버퍼 풀에 있으면 바로 업데이트를 수행하지만, 그렇지 않고 디스크로부터 읽어와서 업데이트해야 한다면 이를 즉시 실행하지 않고 임시 공간에 저장해 두고 바로 사용자에게 결과를 반환하는 방식으로 성능을 향상시킵니다. 이 임시 공간이 바로 체인지 버퍼입니다.
사용자에게 결과를 전달하기 전에 반드시 중복 여부를 체크해야 하는 유니크 인덱스는 체인지 버퍼를 사용할 수 없습니다. 중복 여부를 검사해야 하므로, 유니크 인덱스에 대해서는 즉시 업데이트가 실행됩니다.
체인지 버퍼에 임시로 저장된 인덱스 레코드 조각은 이후 백그라운드 스레드에 의해 병합되며, 이 스레드를 체인지 버퍼 머지 스레드라고 합니다.
🧷4.2.11 리두 로그 및 로그 버퍼
리두 로그는 트랜잭션의 4가지 요소인 ACID 중 D에 해당하는 영속성과 가장 밀접하게 연관돼 있다.
리두 로그는 MySQL 서버가 비정상적으로 종료됐을 때 데이터 파일에 기록되지 못한 데이터를 잃지 않게 해주는 안전장치이다.
MySQL 서버는 데이터 변경 내용을 로그로 먼저 기록한다. 대부분의 DBMS는 쓰기보다 읽기 성능을 고려하기에 데이터 파일 쓰기 과정에서 디스크의 랜덤 엑세스가 필요하다. 그래서 변경된 데이터를 디스크 파일에 기록하는건 매우 큰 비용이 든다.
이로 인한 성능 저하를 막기 위해 데이터베이스 서버는 쓰기 비용이 낮은 자료 구조를 가진 리두 로그를 가지며 비정상 종료 발생 시 복구를 한다.
또한 MySQL은 ACID도 중요하지만 성능도 중요하기에 리두 로그를 버퍼링할 수 있는 리두 버퍼 같은 자료 구조도 가지고 있다.
시나리오
- 커밋되었지만 데이터 파일에 기록되지 않은 데이터
- Redo 로그에 저장된 데이터를 붙여넣기만 하면 된다.
- 롤백했지만 데이터 파일에 이미 기록된 데이터
- 어떤 변경이 커밋/롤백 되었는지, 아니면 트랜잭션 실행 중인지 확인하는데 쓰입니다
기본적으로 리두 로그는 트랜잭션 커밋 시에 바로 디스크에 기록되도록 권장됩니다. 이렇게 함으로써 데이터의 안정성을 확보하고, 트랜잭션이 정상적으로 완료된 이후에도 복구가 가능하도록 합니다.
'CS > DB' 카테고리의 다른 글
[Real MySQL 8.0]8장 - 인덱스와 B-Tree (1/2) (1) | 2024.12.09 |
---|---|
[Real MySQL 8.0]5장 - 트랜잭션 (1) | 2024.12.08 |
[Real MySQL 8.0]4장-1 MySQL 엔진 (1) | 2024.12.05 |
[Real MySQL 8.0]2, 3장 (2) | 2024.11.13 |
[Real MySQL 8.0]1장 (3) | 2024.11.13 |