https://nakabonne.dev/posts/write-tsdb-from-scratch/ - Go로 작성되어 있지만 거의 언어 무관 - 시계열 데이터는 타임스탬프가 붙은 여러 값의 컬렉션. 각 항목은 데이터 포인트 ㅤ→ 양이 많음. 많아야 의미를 가짐. 초당 백만번씩 캡쳐링 되는 경우도 많음 ㅤ→ Append-only, 시간순 정렬, 최근 데이터 우선 ㅤ→ 특정 시간단위 벌크 리딩 ㅤ→ High Cardinality (집합의 단위가 매우 큼) ㅤ→ 대부분 최근 데이터를 읽어서 사용 - 시계열 데이터는 주로 쓰기가 많은 것을 반영한 TStorage DB엔진 라이브러리를 Go 언어로 개발 - 데이터 모델 ㅤ→ 선형 데이터 모델 ㅤ→ 데이터 포인트를 시간단위로 파티셔닝 ㅤ→ 각 파티션은 해당 시간내의 모든 데이터를 가진 별도의 독립적인 DB처럼 동작 ㅤ→ 헤드 와 그 다음 파티션만 Heap에 저장되는 메모리 파티션으로 수정 가능 ㅤ→ 데이터 손실을 막기 위해 쓰기전에 WAL(Write Ahead Log)에 작성 ㅤ→ 그 이전 파티션 데이터들은 디스크에 싱글 파일로 저장. 디스크 파티션들은 읽기 전용 - 메모리 파티션 ㅤ→ 데이터 포인트들의 리스트가 힙상에 배열로 표시 (Go 의 Slice 와 비슷) ㅤ→ 레이턴시 및 동기화 때문에 Out-of-order 가 자주 발생. 같은 파티션 내라면 버퍼링을 통해서 저장할때 재 정렬, 다른 파티션이라면 헤드가 아닌 이전 파티션 뒤에 추가하는 것으로 가능 ㅤ→ WAL 에 실제로 기록되는 것과 똑같은 데이터를 저장해서, 오류시에도 복구 가능하게 - 디스크 파티션 ㅤ→ 파티션당 한개의 디렉토리에 메타 데이터와 압축된 실제 데이터를 저장 (Prometheus V3 Storage 의 축소판) ㅤ→ Memory-Mapped 데이터 형식(커널에서 mmap 으로 캐쉬가능) ㅤ→ 메타데이터는 JSON 형식으로 인덱스를 형성 - 타임스탬프와 밸류 튜플로 표현되는 데이터 인코딩은 페이스북의 Gorilla 논문에서 제안된 인코딩 방식을 사용 ㅤ→ 타임스탬프와 밸류를 서로 다른 메소드로 인코딩 ㅤ→ timestamp 는 unsigned 64-bit integer 값으로 Delta-of-delta 인코딩을 이용 ㅤㅤ✓ Delta 인코딩 : 기존값과 현재값의 차이만 기록하는 방식 ㅤㅤ✓ Delta-of-Delta 인코딩 : 일반적으로 특정 시간당 생기므로 델타의 델타만 기록 ㅤㅤ✓ 가변 길이로 인코딩 되므로 Delta-of-Delta 가 가장 작은 공간을 사용 ㅤ→ values 는 signed 64-bit floating-point 값으로 XOR 인코딩 을 사용 ㅤㅤ✓ 처음 값은 그냥 저장 ㅤㅤ✓ 다음 값을 XOR 해서 0이면 기존 값과 같으므로 0 비트 하나만 저장 ㅤㅤ✓ 0이 아니면 다른 비트들 기반으로 계산(Meaningful Bit) ㅤㅤ✓ 앞/뒤의 0들을 계산해서, 0의 갯수가 같다면 0과 의미있는 비트만 저장, 다르면 리딩 제로 갯수, Meaningful Bit의 갯수과 그 자체를 저장 |