파일 캐시 내부 구조: 캐시 슬라이싱, 다중 큐 관리, 제거 및 워밍업
기본 원리
캐시 슬라이싱과 프리페치 메커니즘
Doris는 대상 파일을 1 MB 경계에 맞춰 슬라이스로 나눕니다. 각 슬라이스의 다운로드가 완료되면 로컬 파일 시스템에 독립적인 블록 파일로 저장됩니다. 이렇게 세분화하면 큰 파일 전체를 캐시하면서 발생하는 공간 낭비를 방지하고, 핫스폿 데이터의 적중 추적과 제거를 더 정밀하게 수행할 수 있습니다.
로컬 파일 디렉터리 구성
캐시 데이터는 여러 디스크의 여러 디렉터리에 분산할 수 있습니다. Doris는 대상 파일 경로의 해시를 계산하고, 이 해시를 블록 파일 경로의 최하위 디렉터리 구성 요소로 사용합니다. 디렉터리 안에서는 각 블록 파일의 이름을 대상 파일 내 오프셋으로 지정하여 데이터를 여러 디렉터리에 고르게 분산합니다.
예: 대상 파일 경로가 /remote/data/datafile1이고 계산된 해시가 12345라면, 해당 블록 파일의 경로는 다음과 같습니다.
/cache/123/12345/<offset>
여기서 <offset>은 원본 파일 내에서 해당 블록 데이터의 오프셋입니다.
다중 큐 메커니즘
Doris는 캐시 오염을 방지하고 적중률을 높이기 위해 캐시 데이터를 유형에 따라 서로 다른 큐로 분류합니다. 아래 표는 우선순위가 높은 큐부터 나열한 것입니다.
| 큐 | 내용 | 우선순위 | 참고 |
|---|---|---|---|
| TTL 큐 | TTL 속성이 설정된 데이터 | 가장 높음 | TTL이 만료되기 전에는 데이터가 제거되지 않습니다. TTL은 테이블 수준 속성입니다. 예를 들어 3600으로 설정하면 데이터 적재 후 최대 1시간 동안 데이터가 캐시에 유지됩니다. 로컬에 데이터를 유지해야 하는 작고 자주 액세스하는 상주 테이블에 적합합니다. |
| Index 큐 | 인덱스 데이터(역색인 제외) | 높음 | 쿼리 필터링을 가속하는 데 사용됩니다. 역색인은 크기가 크므로 Normal 큐에 배치됩니다. |
| Normal 큐 | 일반 데이터(TTL 속성 없음) | 중간 | 대부분의 데이터가 저장되는 큐입니다. |
| Disposable 큐 | 임시 데이터(예: Compaction 중에 읽는 데이터) | 가장 낮음 | 사용 후 가장 먼저 제거됩니다. |
제거 메커니즘
제거가 트리거되는 시점
| 트리거 | 설명 |
|---|---|
| 공간 부족에 따른 수동적 제거 | 로컬 디스크 공간이나 inode가 소진되거나, 캐시 사용량이 설정된 용량 한도에 도달하면 트리거됩니다. |
| 상위 워터마크에서의 능동적 제거 | 캐시 사용량이 상위 워터마크에 도달하면 Doris는 오래된 캐시 항목을 미리 비동기적으로 정리하여 동기식 제거로 인해 쓰기가 차단되는 것을 방지합니다. |
| 가비지 컬렉션에 따른 제거 | Compaction과 Schema Change의 원본 데이터, 데이터 적재 커밋 실패 후 롤백된 데이터, DROP TABLE 또는 DROP PARTITION 작업의 잔여 데이터 등 더 이상 필요하지 않은 데이터를 선제적으로 정리합니다. |
| TTL 만료에 따른 우선순위 하향 | TTL 항목이 만료되면 Normal 큐로 이동하여 일반적인 LRU 제거 절차에 따라 처리됩니다. |
제거 대상 선택 전략
공간 할당 비율: 여러 큐가 디스크 공간을 공유하며, 각 큐에는 독립적인 공간 할당 비율 상한이 있습니다. 예를 들어 Normal 큐의 상한은 전체 공간의 40%입니다. 다른 큐가 할당된 공간을 아직 모두 사용하지 않았다면, 한 큐가 남은 공간을 전부 사용할 수 있습니다. 각 유형의 데이터가 증가함에 따라 실제 사용 비율은 설정된 값에 점차 가까워집니다.
제거 순서: 큐에 쓰기를 수행할 공간이 부족하면 Doris는 다음 순서로 할당 비율을 초과한 데이터를 제거합니다. 각 큐 안에서는 LRU에 따라 먼저 제거할 항목을 결정합니다.
Disposable → Normal → Index → TTL
다른 큐에서 데이터를 제거한 후에도 공간이 부족하면 Doris는 대상 큐 자체의 항목을 LRU에 따라 제거합니다.
핫 데이터 제거를 방지하기 위한 권장 사항
- 충분한 디스크 공간 확보: 캐시 정리에는 어느 정도 지연이 있으므로 항상 여유 공간을 확보하세요. 경험적으로 파일 캐시 용량을 쿼리에서 사용하는 핫 데이터 양의 약 1.5배로 설정하면 높은 적중률을 얻을 수 있습니다.
- 대규모 쿼리 격리: 대규모 쿼리를 전용 클러스터로 라우팅하여 캐시를 점유하고 다른 쿼리의 적중률을 떨어뜨리는 것을 방지하세요.
워밍업 메커니즘
캐시 워밍업은 데이터를 캐시에 미리 로드하여 이후 쿼리가 로컬 캐시에 직접 적중하고 더 나은 쿼리 성능을 얻도록 합니다. Doris는 세 가지 워밍업 방법을 제공합니다.
수동 워밍업
특정 테이블이나 파티션을 지정하거나 다른 클러스터의 캐시 내용을 참조하여 워밍업을 시작할 수 있습니다. 데이터 소스는 항상 원격 스토리지이며, 다른 BE 노드가 아닙니다. 쿼리 경로에서 다른 BE의 캐시를 읽는 기능은 피어 캐시 읽기에서 별도로 제공합니다. 워밍업 절차는 다음과 같습니다.
- 워밍업 명령을 실행합니다. 시스템은 대상을 태블릿 집합으로 변환하고 해당 BE에 전달합니다.
- 각 BE는 담당 태블릿의 모든 데이터 파일을 순차적으로 읽고 데이터를 로컬 파일 캐시에 기록합니다.
- 작업은 각각 최대 20 GB의 배치로 실행됩니다. 각 배치가 완료되면 체크포인트를 저장하므로 중단 후 재개할 수 있습니다.
- BE가 중단되거나 사용자가 작업을 수동으로 취소하면 모든 BE가 다운로드를 중지하고 워밍업이 종료됩니다.
작업 상태(FINISHED / CANCELLED / RUNNING)와 전체 진행률은 SHOW WARM UP JOB으로 확인할 수 있습니다. 같은 테이블과 파티션을 다시 워밍업하면 Doris는 기존 데이터를 자동으로 감지하고, 이미 캐시된 데이터를 다시 다운로드하지 않고 증분 업데이트를 수행합니다.
데이터 재조정으로 트리거되는 워밍업
확장 또는 노드 장애에 따른 부하 재조정으로 태블릿이 새 BE로 마이그레이션되면, 새 BE는 기존 BE에 RPC를 보내 캐시 메타데이터를 가져옵니다. 그런 다음 해당 메타데이터를 기반으로 데이터를 로컬 파일 캐시에 다시 다운로드합니다. 태블릿 정보가 정리될 때 기존 노드의 캐시 데이터도 선제적으로 제거되어 공간이 확보됩니다.
참고: 마이그레이션이 완료된 시점부터 캐시 다운로드가 준비될 때까지 시간 차이가 있으며, 이 기간에는 파일 캐시 미스가 발생할 수 있습니다. 명시적으로 활성화해야 하는 피어 캐시 읽기를 사용하면 이 기간에 새 BE가 원격 스토리지에 다시 액세스하는 대신 원본 BE의 캐시에서 누락된 블록을 직접 가져올 수 있습니다.
컴퓨팅 클러스터 간 자동 워밍업(버전 3.1+)
컴퓨팅과 스토리지가 분리된 배포에서는 여러 컴퓨팅 클러스터의 파일 캐시를 자동으로 동기화할 수 있습니다. Doris는 두 가지 동기화 방법을 제공합니다.
| 방법 | 사용 시점 | 설명 |
|---|---|---|
| 주기적 워밍업 | 데이터의 실시간 최신성이 중요하지 않을 때 | WARM UP SQL 문에서 동기화 간격을 지정합니다. 작업은 지정된 테이블과 파티션의 증분 데이터를 대상 클러스터로 주기적으로 동기화합니다. |
| 데이터 적재/Compaction으로 트리거되는 워밍업 | 실시간 최신성이 필요할 때 | 데이터 적재 작업이 커밋 단계에 진입하면 원본 클러스터의 BE가 대상 클러스터의 해당 BE에 새로 업로드된 데이터를 다운로드하도록 알립니다. Compaction 완료 시에도 비슷한 알림 경로가 트리거됩니다. FE는 대상 클러스터의 태블릿 분포 정보를 관리합니다. |
시나리오 분석
쿼리 시나리오
쿼리 중 파일 캐시 처리 절차는 다음과 같습니다.
- Scanner 읽기 요청: 쿼리가 도착하면 Scanner 컴포넌트가 필요한 데이터 파일을 읽을 준비를 합니다.
- 로컬 캐시 확인: Scanner는 먼저 로컬 파일 캐시를 확인하고, 파일 경로와 오프셋으로 캐시 메타데이터를 대조합니다.
- 캐시 적중: 일치하는 캐시 데이터가 발견되면 시스템은 BlockFile 핸들 집합을 반환하고, Scanner는 원격 스토리지에 액세스하지 않고 로컬에서 데이터를 읽습니다.
- 캐시 미스: 캐시에 없는 범위에 대해서는 Scanner가 원격 스토리지에서 데이터를 다운로드하고 파일 캐시에 기록한 다음 반환합니다. 공간은 제거 정책에 따라 확보됩니다. 피어 캐시 읽기가 명시적으로 활성화되어 있으면 Scanner는 먼저 다른 BE의 파일 캐시를 확인하고, 데이터를 가진 BE가 없을 때만 원격 스토리지에서 다운로드합니다.
데이터 적재 시나리오
데이터 적재 중 파일 캐시 처리 절차는 다음과 같습니다.
- 원격 스토리지에 업로드: 적재된 데이터를 먼저 원격 스토리지에 기록합니다.
- 로컬 캐시에 비동기 쓰기: 동시에 Doris는 데이터를 로컬 파일 캐시에 비동기적으로 기록하여 데이터 적재 직후의 쿼리가 캐시에 직접 적중하도록 합니다.
- 캐시 큐 할당: 데이터 유형과 TTL 속성에 따라 해당 큐(TTL 큐, Index 큐 또는 Normal 큐)에 데이터를 기록합니다.
Compaction 시나리오
Doris에는 두 가지 유형의 Compaction이 있습니다.
- Cumulative Compaction: 증분 데이터 세그먼트를 병합합니다.
- Base Compaction: 기준 데이터 버전(버전 0부터 시작)을 증분 데이터 버전과 병합합니다.
두 Compaction 유형은 서로 다른 캐시 쓰기 전략을 사용합니다.
| Compaction 유형 | 캐시 쓰기 전략 |
|---|---|
| Cumulative Compaction | 데이터 적재 절차와 동일하게, 출력 데이터를 원격 스토리지에 업로드하면서 파일 캐시에도 기록하여 이후 쿼리를 가속합니다. |
| Base Compaction | 기본적으로 대량의 콜드 데이터로 인한 캐시 오염을 방지하기 위해 캐시 공간이 충분할 때만 데이터를 캐시에 기록합니다. BE 매개변수 enable_file_cache_keep_base_compaction_output = true를 설정하면 강제 쓰기가 가능하지만, 이로 인해 다른 핫 데이터가 제거될 수 있습니다. |
로컬 캐시 용량이 제한된 경우 인덱스만 쓰는 정책을 활성화하여 Compaction 출력의 Segment 데이터가 캐시 공간을 대량으로 점유하는 것을 방지할 수 있습니다.
enable_file_cache_write_index_file_only=true: Compaction을 포함하여 컴퓨팅과 스토리지 분리 모드의 모든 rowset 쓰기에 적용됩니다. Segment 데이터는 능동적으로 캐시하지 않지만, Segment 푸터/내부 인덱스 범위와 독립적인 역색인 파일은 계속 캐시에 기록합니다.enable_file_cache_write_base_compaction_index_only=true또는enable_file_cache_write_cumu_compaction_index_only=true: 해당 설정이 없으면 캐시에 기록될 Compaction 출력 중 일치하는 유형에만 제한을 적용합니다. Segment 파일은 건너뛰고 독립적인 역색인 파일은 유지하지만, Segment 푸터/내부 인덱스 범위는 미리 로드하지 않습니다.
매개변수 우선순위, 구성 예시 및 제한 사항은 파일 캐시 구성을 참조하세요.
계획: 향후 Doris 버전에서는 과거 쿼리 통계를 기반으로 하는 적응형 쓰기 전략을 제공할 예정입니다.
재시작 후 캐시 로딩
버전 3.1 이전에는 재시작 후 LRU 큐 순서를 복원할 수 없어 핫 데이터가 제거되고 적중률이 저하될 수 있었습니다.
버전 3.1에서는 LRU 정보 영속화가 도입되었습니다. 로딩 절차는 다음과 같습니다.
- 주기적 덤프: 각 LRU 큐의 순서 정보를 디스크에 주기적으로 저장합니다.
- 재시작 시 로드: 노드가 재시작되면 디스크에서 덤프 데이터를 읽어 캐시 큐 상태를 복원합니다.
- 데이터 누락 방지를 위한 전체 디스크 스캔: 덤프 시점의 시간 차이로 인해 발생하는 메타데이터와 디스크 간 불일치를 바로잡기 위해 재시작 후 전체 디스크를 스캔하여 데이터 무결성을 보장합니다.
- 쿼리 요청에 따른 동시 비동기 로딩: 전체 디스크 스캔 중에도 BE는 쿼리를 처리할 수 있습니다. 쿼리가 아직 스캔되지 않은 데이터를 요청하면 시스템은 쿼리 지연 시간을 줄이기 위해 해당 데이터를 미리 로드합니다.
확장 및 축소 시나리오
수평 확장
Doris는 재조정을 통해 태블릿을 새 BE 노드로 마이그레이션합니다. 대상 BE는 원본 BE의 캐시 메타데이터를 기반으로 데이터를 로컬 스토리지에 다시 다운로드하여 새 노드의 쿼리도 파일 캐시에 적중할 수 있도록 합니다. 피어 캐시 읽기를 사용하면 다운로드가 완료되기 전에도 대상 BE의 초기 읽기 요청이 원본 BE의 캐시에서 블록을 직접 가져올 수 있습니다.
수평 축소
태블릿 재조정 후 클러스터의 전체 파일 캐시 용량이 실제 캐시된 데이터 양보다 작아지면 제거 메커니즘이 초과 데이터를 자동으로 제거합니다.
수직 확장
| 확장 방법 | 지침 |
|---|---|
| 단일 디스크 용량 증가 | 다음 명령을 실행하여 공간 변경을 BE에 알립니다. curl http://<BE_IP>:<WEB_PORT>/api/file_cache?op=reset&capacity=<new capacity in bytes> |
| 디스크 추가 | 권장하지 않습니다. Doris는 현재 재해싱을 구현하지 않았으며 디스크 간 재조정을 지원하지 않습니다. 캐시 디렉터리 수를 변경하면 캐시 조회에 실패할 수 있습니다. 디스크를 추가해야 한다면 캐시를 비우고 필요에 따라 다시 워밍업하세요. |
수직 축소
디스크 공간을 줄일 때도 위의 reset 명령을 실행해야 합니다. 캐시 용량이 실제 캐시된 데이터 양보다 작아지면 제거 메커니즘이 트리거되어 데이터를 자동으로 정리합니다.
확장 및 축소 후 워밍업 고려 사항
수평 확장과 수평 축소에는 태블릿 재조정 작업이 수반됩니다. 워밍업이 효과적으로 수행되도록 마이그레이션이 안정화된 후 워밍업을 실행하세요. FE 메트릭 doris_fe_tablet_num을 모니터링하여 마이그레이션 완료 여부를 판단할 수 있습니다. 곡선이 변동 없이 평탄하고 안정적이면 마이그레이션이 완료되었으며 안전하게 워밍업을 실행할 수 있다는 의미입니다.
자주 묻는 질문
질문: 캐시 적중률이 낮고 쿼리 성능이 개선되지 않았습니다. 어떻게 문제를 해결하나요?
- 파일 캐시 디스크에 충분한 여유 공간이 있는지 확인하세요. 권장 캐시 용량은 핫 데이터 양의 최소 1.5배입니다.
- 대규모 쿼리가 핫 데이터를 자주 제거하는지 확인하세요. 대규모 쿼리를 격리하는 방안을 고려하세요.
- 재시작 후 잠시 적중률이 낮은 것은 정상입니다. LRU 복구에는 시간이 걸립니다. 미리 수동 워밍업을 실행할 수 있습니다.
질문: 수평 확장 후 쿼리에서 캐시 미스가 발생합니다. 어떻게 해야 하나요?
- 태블릿 재조정이 완료되었는지 확인하세요.
doris_fe_tablet_num곡선이 평탄하고 안정적이어야 합니다. - 대상 테이블과 파티션에 대해 수동 워밍업 명령을 실행하세요.
- 수평 확장 후 새 BE에서 데이터를 다운로드하는 데는 시간이 걸립니다. 이 기간에 캐시 미스가 발생하는 것은 정상입니다. 피어 캐시 읽기를 명시적으로 활성화하면 이 기간의 영향을 줄일 수 있으므로 활성화 여부를 확인하세요.
질문: Base Compaction으로 인해 핫 데이터가 제거되나요?
- 기본적으로 Base Compaction은 공간이 충분할 때만 캐시에 기록하며, 핫 데이터를 능동적으로 제거하지 않습니다.
enable_file_cache_keep_base_compaction_output = true가 활성화되어 있으면 Base Compaction 데이터가 캐시에 강제로 기록되므로 핫 데이터가 제거될 수 있습니다. 실제 워크로드를 기준으로 이 절충점을 평가하세요.
질문: TTL이 만료되면 데이터가 즉시 삭제되나요?
- 아니요, 즉시 삭제되지는 않습니다. TTL이 만료되면 데이터는 Normal 유형으로 전환되어 Normal 큐에 들어가고 일반적인 LRU 제거 절차에 따라 처리됩니다. 최종적으로 데이터가 제거되는 시점은 제거 메커니즘에 의해 결정됩니다.
질문: 수동 워밍업 시 이미 캐시된 데이터를 다시 다운로드하지 않을 수 있나요?
- 네. 워밍업을 실행하면 Doris는 이미 캐시된 데이터를 자동으로 식별하고, 새 데이터나 변경된 데이터에 대해서만 증분 다운로드를 수행합니다.