한국어

개발자 도구 · UUID 생성기

무작위 UUID 기본 키 및 B-트리 조각화: 실제로 일어나는 일

· 그것이 중요한 이유

uuid 암호화 브라우저 API

한 페이지를 채우는 순차 삽입과 여러 페이지에 분산된 무작위 삽입을 보여주는 B-트리 다이어그램
원본 ToolAcre 벡터 일러스트레이션

임의 v4 키가 임의 인덱스 페이지에 삽입되고 클러스터형 인덱스가 이에 대한 비용을 지불합니다. 이 게시물에서는 메커니즘, 텍스트 대 바이너리의 저장 비용, 시간 순서 UUID가 그림을 변경하는 위치에 대해 설명합니다.

테이블이 커짐에 따라 삽입 속도가 느려집니다. 이는 DBA가 키 선택을 살펴보게 만드는 증상입니다.

클러스터형 인덱스가 있는 데이터베이스에 기본 키로 임의의 UUID이 있는 행을 삽입하면 데이터베이스가 B-트리 구조 내의 임의의 위치에 새 행을 삽입하게 됩니다. 순차 키는 가장 오른쪽 리프 페이지에 추가되어 모든 삽입을 작은 캐시 상주 영역 내에 유지합니다. 무작위 키는 전체 인덱스에 삽입을 분산시켜 데이터베이스가 물리적 스토리지에서 멀리 떨어져 있는 페이지를 이동하고 수정하도록 합니다. 테이블이 커지고 트리가 깊어짐에 따라 모든 삽입이 더 많은 페이지에 닿고 더 많은 I/O 작업이 발생합니다. 천 행에서는 저렴해 보였던 작업이 백만 행에서는 비용이 많이 듭니다. 이것은 이론적인 문제가 아닙니다. 이는 삽입 처리량의 측정 가능한 저하로 나타납니다.

클러스터링된 B-트리를 채우는 방법 — 순차 키가 마지막 페이지에 추가됩니다. 임의의 키가 전체 인덱스의 페이지를 터치합니다.

메커니즘은 리프 페이지 내에서 정렬된 키 순서를 유지하기 때문에 B-트리 작동 방식의 기본입니다. 10,001 키가 있는 행을 이미 1 ~ 10,000 키가 저장된 테이블에 삽입하면 데이터베이스는 해당 행이 어디에 속하는지, 즉 마지막에, 공간이 있는 경우 가장 오른쪽에 있는 기존 페이지에, 아니면 오른쪽에 추가된 새 페이지에 있는지 알게 됩니다. 7524fae2-7dec-11d0-a765-00a0c91e6bf6과 같은 임의의 UUID이 있는 행을 동일한 테이블에 삽입하면 데이터베이스는 트리를 탐색하여 해당 UUID 범위의 키가 포함된 리프 페이지를 찾고 해당 페이지 내에서 정확한 위치를 찾아 행을 삽입해야 합니다. 해당 페이지가 가득 차면 분할되어 내용의 절반을 새 페이지로 이동하고 상위 노드를 업데이트합니다.

페이지 분할 및 캐시 압력 — 임의 삽입에 더 많은 비용이 드는 이유 I/O 및 테이블 크기에 따라 효과가 커지는 이유

임의 키는 모든 삽입이 가장 오른쪽 페이지에 추가되는 대신 트리의 임의 위치에 놓이기 때문에 최악의 삽입 패턴을 생성합니다. 데이터베이스는 올바른 페이지를 검색해야 하며, 이를 위해서는 일반적으로 트리 수준당 하나씩 여러 페이지를 읽어야 합니다. 그런 다음 해당 페이지를 수정해야 하며, 이로 인해 트리 위로 전파되는 분할이 트리거될 수 있습니다. 더 많은 페이지가 수정되고, 더 많은 쓰기가 발생하며, 메모리 버퍼 풀은 활성 삽입 지점에 집중하는 대신 트리의 다른 영역에 있는 페이지로 채워집니다. 캐시 미스가 증가하고 I/O이(가) 병목 현상을 일으키며 테이블이 더 큰 규모로 성장함에 따라 삽입 속도가 정체됩니다.

텍스트 또는 이진 — 36 문자 문자열과 16바이트 기본 uuid 또는 이진(16) 열 및 인덱스 크기에 미치는 영향

시스템마다 페이지 관리를 다르게 최적화하므로 비용은 데이터베이스 엔진 전체에 걸쳐 동일하지 않습니다. 공격적인 압축, 작은 페이지 크기 또는 메모리 내 작업을 사용하는 데이터베이스에서는 순차 키와 임의 키 간의 성능 차이가 더 작을 수 있습니다. 큰 페이지, 기계적 디스크 또는 엄격한 메모리 제한이 있는 데이터베이스는 급격한 성능 저하를 나타냅니다. 문제는 관찰 가능하고 측정 가능합니다. 1,000 행, 100,000 행 및 1,000,000 행에서 삽입 비율을 측정합니다. 더 큰 규모에서 초당 속도가 급격하게 떨어지면 특정 하드웨어 및 데이터베이스 구성에 무작위 삽입 패널티가 발생하는 것입니다.

시간순 대안 — UUIDv7 및 ULID가 인덱스 끝에 삽입하는 동안 고유성을 유지하는 방법

문자열인 UUID는 선택한 저장 형식에 따라 텍스트 형식의 36 문자 또는 바이너리 UUID 유형의 16 바이트를 사용합니다. UTF-8 또는 ASCII 열의 36 문자 문자열은 36바이트인데 비해 64비트 정수의 경우 8바이트입니다. 문자열-UUID 열의 인덱스는 압축 기술이 적용되지 않는다고 가정할 때 정수 열의 인덱스보다 3배 더 큽니다. 인덱스가 클수록 버퍼 풀에 맞는 인덱스 페이지 수가 적어지며, 이는 트리를 순회할 때 캐시 적중 횟수가 줄어드는 것을 의미합니다. RAM에 맞는 작은 인덱스는 삽입 순서나 작업 패턴에 관계없이 모든 쿼리에서 디스크에서 읽어야 하는 큰 인덱스보다 더 나은 성능을 발휘합니다.

작업된 예 — 고안된 벤치마크 없이 질적으로 무작위 키 및 시간 순서 키 테이블에 대해 설명된 동일한 삽입 작업 부하

기본 키를 포함하는 모든 보조 인덱스는 전체 36 문자 UUID 또는 16바이트 바이너리 UUID 값을 저장해야 하기 때문에 스토리지 차이는 기본 인덱스와 보조 인덱스 모두에 매우 중요합니다. 이로 인해 기본 키 조회에 정수 키를 사용하는 인덱스보다 보조 인덱스가 훨씬 더 커집니다. 복제, 백업 및 쿼리 결과 세트는 모두 인덱스 크기가 커짐에 따라 비례적으로 증가합니다. ToolAcre UUID 생성기는 바이너리 호환 값을 생성합니다. 데이터베이스에 따라 이를 바이너리(16) 또는 GUID 유형으로 저장하면 varchar(36)에 비해 공간이 절약되고 전반적으로 캐시 효율성이 향상됩니다. 이러한 스토리지 최적화는 대규모 시스템에 매우 중요합니다.

여기서 다루지 않는 내용 — 기본 키가 클러스터되지 않고 효과가 더 작은 힙 구성 테이블 및 데이터베이스

일반적인 최적화는 UUID을 내부적으로 바이너리로 저장하고 API 또는 사용자 인터페이스에 필요할 때만 문자열로 표시하는 것입니다. 인덱스 및 조인 작업은 압축 바이너리 형식으로 작동합니다. API 응답 또는 애플리케이션 코드는 문자열 표현으로 변환됩니다. 일부 데이터베이스는 이 변환을 자동으로 처리하는 기본 제공 GUID 또는 UUID 유형을 제공합니다. 다른 것들은 명시적인 캐스팅 작업이 필요합니다. 36바이트 열과 16바이트 열 사이의 성능 차이는 실제입니다. 백만 개의 행과 36바이트가 있는 테이블과 16바이트 UUID 열 키는 인덱스 수준당 20 MB만큼 다릅니다. 이는 L3 캐시에 맞는 인덱스와 메모리 가져오기가 필요한 사이의 차이일 수 있습니다.

요점: 버전을 선택하기 전에 색인을 알아두십시오. ToolAcre 생성기는 임의의 UUID를 생성합니다. 게시물을 사용하여 스토리지 엔진에 적합한지 결정하세요.

삽입 성능, 인덱스 크기 및 쿼리 특성 간의 균형을 유지하려면 워크로드 패턴을 기반으로 한 아키텍처 결정이 필요합니다. 예를 들어 타임스탬프 기반 문자열과 같이 문자열이 오름차순인 경우 순차적 문자열 키를 삽입하는 것이 빠르지만 임의 UUID와 동일한 공간을 소비하고 시간 정보가 누출됩니다. 임의의 UUID은 의미상 더 깨끗하고 누출될 타임스탬프 구성 요소가 없지만 클러스터형 인덱스에 삽입하는 속도가 느리고 전체적으로 스토리지가 더 큽니다. UUIDv7과 같은 시간순 대안은 4 버전 무작위 식별자에서 타임스탬프 누출을 피하면서 삽입 위치를 유지함으로써 이점을 결합합니다.