VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
페이지: 저장의 단위
InnoDB는 모든 것을 페이지에 저장합니다 — 기본적으로 16KB의 고정 크기 덩어리입니다. 페이지는 InnoDB가 디스크에서 읽거나 디스크에 쓰는 가장 작은 단위입니다. 행 하나를 읽을 때 InnoDB는 그 행이 들어 있는 16KB 페이지 전체를 로드합니다. 이것은 실질적인 결과를 낳습니다:- 연속된 행을 읽는 쿼리(범위 스캔, 정렬된 데이터에 대한 전체 테이블 스캔)는 각 페이지를 한 번 로드하고 그 페이지에서 많은 행을 처리합니다
- 무작위 행을 읽는 쿼리(큰 테이블에 대한 보조 인덱스 사용)는 행마다 새 페이지를 로드하며, 각각이 별도의 디스크 읽기일 가능성이 큽니다
클러스터형 인덱스
InnoDB는 테이블 행을 클러스터형 인덱스에 저장합니다 — 리프 노드에 실제 행 데이터가 들어 있고 기본 키로 물리적으로 정렬된 B-트리입니다. 별도의 “테이블 파일”과 “기본 키 인덱스 파일”은 없습니다 — 둘은 같은 구조입니다.id 순서로 물리적으로 저장됩니다. WHERE id BETWEEN 1000 AND 2000 같은 쿼리는 연속된 페이지 범위를 읽습니다 — 빠릅니다. id = 1, 그다음 id = 500000, 그다음 id = 250인 행을 가져오는 쿼리는 물리적으로 흩어진 세 페이지를 읽습니다.
기본 키를 정의하지 않으면 InnoDB는 숨겨진 6바이트 행 ID를 만들어 클러스터형 인덱스 키로 사용합니다. 이것은 최적화 기회를 낭비합니다.
보조 인덱스
모든 보조 인덱스는 별도의 B-트리입니다. 리프 노드는 전체 행을 저장하지 않습니다 — 인덱싱된 열 값과 기본 키를 저장합니다. 보조 인덱스에 없는 열을 가져오기 위해 InnoDB는 행 조회를 합니다: 보조 인덱스 리프에서 기본 키를 가져와 클러스터형 인덱스에서 전체 행을 가져옵니다.버퍼 풀
InnoDB는 페이지를 버퍼 풀이라는 메모리에 캐시합니다. 디스크에서 읽은 페이지는 버퍼 풀에 로드되어 다른 것에 메모리가 필요해질 때까지 거기에 머뭅니다. 같은 페이지에 대한 이후 읽기는 RAM에서 처리됩니다.innodb_buffer_pool_size를 사용 가능한 RAM의 70–80%로 설정하세요. 이것은 읽기 위주 워크로드에서 가장 영향력이 큰 단일 구성 변경입니다 — 작업 집합이 메모리에 많이 들어갈수록 디스크 읽기가 줄어듭니다.
행 형식
InnoDB는 네 가지 행 형식을 지원합니다. 기본값은DYNAMIC입니다.
기본 16KB 페이지의 대략 절반인 ~8KB 수치는 InnoDB가 가변 길이 열 데이터를 페이지 외부로 옮기는 지점이므로, 큰
VARCHAR/TEXT/BLOB 열이 몇 개뿐인 행은 상한에 부딪히지 않고 그것을 훨씬 넘어 커질 수 있습니다. 그러나 페이지 외부로 옮겨지지 않고 인라인으로 저장되는 열 — 예를 들어 많은 고정 폭 CHAR 열 — 은 여전히 InnoDB 자체의 행 크기 제한에 전부 계산되며, 그 제한에 도달하면 ERROR 1118 (42000): Row size too large (> 8126). Changing some columns to TEXT or BLOB may help.가 발생합니다. MySQL의 별도이고 더 높은 행 크기 제한은 BLOB/TEXT 오버플로 페이지를 제외하고 65,535바이트이며, ROW_FORMAT과 관계없이 적용됩니다. 두 제한 모두 실제이며, 어떤 열이 크고 그것이 페이지 외부로 옮겨질 수 있는지에 따라 어느 쪽이든 먼저 부딪히는 제한이 될 수 있습니다.
DYNAMIC 형식에서 VARCHAR, TEXT 또는 BLOB 열이 페이지를 넘치면 전체 값이 오버플로 페이지에 저장됩니다 — 메인 행에는 20바이트 포인터만 남습니다. COMPACT는 대신 처음 768바이트를 인라인으로 저장합니다. DYNAMIC의 접근 방식은 긴 값이 있을 때 메인 행을 더 작게 유지하므로, 페이지당 더 많은 행이 들어가고 B-트리가 더 얕게 유지됩니다.
자주 묻는 질문
기본 키 선택이 왜 성능에 그렇게 큰 영향을 주나요?
클러스터형 인덱스가 행을 기본 키로 물리적으로 정렬하기 때문입니다. 무작위 위치에 삽입(UUID 기본 키 같은)하면 페이지 분할이 일어납니다 — InnoDB는 순서를 유지하기 위해 가득 찬 페이지를 분할하고 데이터를 옮겨야 합니다. 순차적 기본 키(AUTO_INCREMENT, UUID v7)는 끝에 삽입되어 분할을 피합니다. 페이지 분할은 테이블스페이스를 단편화하고 시간이 지남에 따라 쓰기와 읽기를 모두 느리게 합니다.버퍼 풀이 너무 작으면 어떻게 되나요?
InnoDB는 디스크에서 페이지를 자주 읽어야 합니다.Innodb_buffer_pool_reads가 올라가는 것을 보게 됩니다. 서버는 I/O 바운드가 됩니다 — RAM에서라면 마이크로초가 걸릴 쿼리가 디스크에서는 밀리초가 걸립니다. 읽기 위주 워크로드에서는 작업 집합을 담는 버퍼 풀이 거의 어떤 인덱스 최적화보다 가치 있습니다.
InnoDB와 MyISAM의 차이는 무엇인가요?
InnoDB는 트랜잭션, 외래 키, 행 수준 잠금, 충돌 복구를 지원합니다. MyISAM은 이 중 어느 것도 지원하지 않습니다 — 특정 읽기 전용 대량 워크로드에는 더 빠르지만 대부분의 애플리케이션에는 적합하지 않습니다. InnoDB가 MySQL 기본값이며, 특별한 이유가 없다면 그것을 사용하세요.문제 해결
VillageSQL은 같은 InnoDB 엔진을 탑재하므로, 이 페이지의 모든 설정이 그대로 적용됩니다. VillageSQL을 설치하여 InnoDB를 유지하면서 사용자 정의 타입, 함수, 인덱스를 위한 확장 프레임워크를 얻으세요.
참고
- MySQL 인덱스의 동작 방식 — InnoDB 페이지 안에서 B-트리 인덱스가 배치되는 방식
- MySQL의 커버링 인덱스 — 인덱스 안에 머무는 것이 비싼 페이지 읽기를 피하는 이유
- MySQL의 테이블 파티셔닝 — 여러 InnoDB 세그먼트에 데이터 분할하기