Kafka의 색인 생성 메커니즘
Kafka에서 각 로그 세그먼트 파일은 두 개의 인덱스 파일, 즉 오프셋 인덱스 파일과 타임스탬프 인덱스 파일(자세히 나열되지 않은 트랜잭션 로그 인덱스 파일 등이 있습니다.)에 해당하며 주로 사용됩니다. 메시지 찾기의 효율성을 향상시킵니다.
오프셋 인덱스 파일은 메시지 오프셋(오프셋)과 물리적 주소 사이의 매핑 관계를 설정하는 데 사용됩니다. 이를 통해 타임스탬프 인덱스 파일을 기반으로 하는 메시지의 물리적 파일 위치를 빠르게 찾을 수 있습니다. 지정된 시간. 스탬프(timestamp)를 사용하여 해당 오프셋 정보를 찾습니다.
Kafka의 인덱스 파일은 희소 인덱스 형식으로 메시지 인덱스를 구성합니다. 이는 각 메시지가 인덱스 파일에 해당 인덱스 항목이 있다는 것을 보장하지 않습니다.
일정량의 메시지(브로커 측 매개변수 log.index.interval.bytes로 지정, 기본값은 4096, 즉 4KB)가 기록될 때마다 오프셋 인덱스 파일과 타임스탬프 인덱스 파일은 별도로 저장됩니다. 오프셋 인덱스 항목과 타임스탬프 인덱스 항목을 추가하고, log.index.interval.bytes의 값을 늘리거나 줄이고, 그에 따라 인덱스 항목의 밀도를 줄이거나 늘립니다.
희소 인덱스는 MappedByteBuffer를 통해 인덱스 파일을 메모리에 매핑하여 인덱스 쿼리 속도를 높입니다.
오프셋 인덱스 파일의 오프셋이 단조 증가합니다. 지정된 오프셋을 쿼리할 때 이진 검색 방법을 사용하면 지정된 오프셋이 인덱스 파일에 없는 경우 최대 오프셋이 줄어듭니다. 지정된 오프셋보다 반환됩니다.
타임스탬프 인덱스 파일의 타임스탬프도 엄격한 단조 증가를 유지합니다. 지정된 타임스탬프를 쿼리할 때 이진 검색 방법을 사용하여 타임스탬프보다 크지 않은 최대 오프셋을 찾습니다. 해당 실제 파일 위치도 오프셋 인덱스 파일을 기반으로 재배치해야 합니다.
희소 인덱스 방법은 디스크 공간, 메모리 공간, 검색 시간 및 기타 측면을 절충한 것입니다.
자세한 분석을 위해 오프셋 인덱스 파일을 사용하세요. 오프셋 인덱스 항목의 형식은 아래 그림과 같습니다.
각 인덱스 항목은 8바이트를 차지하며 두 부분으로 나뉩니다.
(1)relativeOffset: 상대 오프셋, baseOffset을 기준으로 한 메시지의 오프셋을 나타냅니다. 4바이트를 차지하며, 현재 인덱스 파일의 파일 이름은 baseOffset 값입니다.
(2) 위치: 로그 세그먼트 파일에서 메시지의 해당 물리적 위치인 물리적 주소로, 4바이트를 차지합니다.
메시지의 오프셋은 8바이트를 차지하며 절대 오프셋이라고도 할 수 있습니다.
인덱스 항목에 절대 오프셋을 직접 사용하는 대신 4바이트 상대 오프셋(relativeOffset = offset - baseOffset)만 차지하므로 인덱스 파일이 차지하는 공간을 줄일 수 있습니다.
예를 들어 로그 세그먼트의 baseOffset이 32인 경우 해당 파일 이름은 00000000000000000032.log입니다. 인덱스 파일에서 오프셋이 35인 메시지의relativeOffset 값은 35-32=3입니다. .
오프셋이 23인 메시지를 찾으려면 먼저 이분법을 통해 오프셋 인덱스 파일에서 23보다 크지 않은 가장 큰 인덱스 항목, 즉 [22, 656 ], 그런 다음 로그 세그먼트 파일의 물리적 위치 656에서 시작하여 오프셋 23에서 메시지를 순차적으로 찾습니다.
위의 경우가 가장 간단한 경우입니다. 위 그림을 참고하면 오프셋이 268인 메시지를 찾으려면 어떻게 해야 할까요?
먼저 baseOffset이 251인 로그 세그먼트를 찾은 다음 상대 오프셋을 계산해야 합니다. relativeOffset = 268 - 251 = 17, 그리고 해당 인덱스 파일에서 17보다 크지 않은 인덱스 항목을 찾습니다. 마지막으로 인덱스 항목의 위치에 따라 특정 로그 세그먼트 파일 위치를 찾아 대상 메시지 검색을 시작합니다.
그러면 baseOffset이 251인 로그 세그먼트를 어떻게 찾나요?
이것은 순차 검색이 아니라 점프 테이블 구조입니다.
Kafka의 각 로그 객체에서는 각 로그 세그먼트를 저장하기 위해 ConcurrentSkipListMap을 사용합니다. 각 로그 세그먼트의 baseOffset을 키로 사용하므로 이를 기반으로 메시지가 있는 로그 세그먼트를 빠르게 찾을 수 있습니다. 지정된 오프셋 부분.
Kafka에서 메시지를 찾으려면 먼저 오프셋에 따라 ConcurrentSkipListMap에서 해당(baseOffset) 로그 세그먼트의 인덱스 파일을 찾은 다음 오프셋 인덱스 인덱스 파일을 읽은 다음 바이너리 분할 방법을 사용합니다. : 오프셋 인덱스 파일에서 offset - baseOffset z보다 크지 않은 가장 큰 인덱스 항목을 찾은 후, 로그 세그먼트 파일을 읽어 로그 세그먼트 파일에서relativeOffset에 해당하는 메시지를 순차적으로 검색합니다.
Kafka의 오프셋을 통해 메시지 내용을 쿼리하는 전체 프로세스는 다음 그림으로 단순화될 수 있습니다.
Kafka의 메시지 오프셋은 InnoDB의 기본 키와 비교할 수 있습니다. 전자는 오프셋을 통해 전체 Record의 데이터를 검색하고, 후자는 기본키를 통해 전체 Record의 데이터를 검색한다.
InnoDB에서 기본 키를 통해 데이터 내용을 조회하는 전체 과정을 다음 그림(하단)으로 단순화하는 것이 좋습니다.
Kafka에서 타임스탬프 인덱스 파일을 통해 메시지를 검색하는 방법은 InnoDB의 보조 인덱스 검색 방법과 비교할 수 있습니다.
전자는 타임스탬프를 통해 오프셋을 찾는 것이고, 후자는 통과한다. 인덱스는 기본키를 찾는다. 후자 2개의 과정은 위의 문장과 같다.
Kafka에서는 데이터가 기록될 때마다 ConcurrentSkipListMap이 업데이트되지 않고 새 인덱스 파일이 생성될 때 업데이트됩니다. 이 영역의 유지 관리 양은 기본적으로 무시할 수 있습니다.
데이터가 있을 때. B+ 트리에서 삽입, 업데이트 또는 삭제되면 인덱스도 업데이트되어야 하며, 이로 인해 "페이지 분할"과 같은 상대적으로 시간이 많이 걸리는 작업이 발생하게 됩니다. Kafka의 인덱스 파일 역시 파일을 순차적으로 추가하는 작업으로 B+ 트리에 비해 작업량이 훨씬 적습니다.
최종 분석에서는 다양한 애플리케이션 시나리오에 따라 결정됩니다. MySQL에서는 CRUD 작업을 자주 수행해야 하는데, CRUD는 MySQL의 주요 작업 내용이므로 이를 지원하려면 훨씬 더 많은 유지 관리 작업이 필요한 B+ 트리를 사용해야 합니다.
Kafka의 메시지는 일반적으로 디스크에 순차적으로 기록된 다음 디스크에서 순차적으로 읽습니다(페이지 캐시에 들어가지 않는 등). 그의 주요 작업 내용은 쓰기 + 읽기이며 거의 없습니다. 검색 쿼리 작업
즉, 검색 쿼리는 Kafka의 보조 기능일 뿐이며 이 기능에 대한 상위 수준 인덱스를 유지하기 위해 너무 많은 비용을 지출할 필요가 없습니다.
앞서 언급했듯이 Kafka에서 이 방법은 디스크 공간, 메모리 공간, 검색 시간 및 기타 측면을 절충한 것입니다.