기록해야 기억에 남는다
채팅을 파일로 저장하는 법 (2) 본문
(1) IndexElement, TextElement
(1) IndexElement
- 채팅 생성 timeline (4bytes), file offset (4bytes) 로 구성
- .idx파일에 8bytes 블록 단위로 기록
- timeline값을 기준으로 file offset 값을 읽어올 수 있음
- 기존 설계의 경우 timeline 8bytes, file offset 8bytes 총 16bytes 였으나, 채팅 데이터 양이 그렇게 많지 않고 데이터를 줄이기 위해 int 2개로 처리
- 이론상 file offset 2^31 - 1 bytes 까지 기록 가능 (이후 overflow가 발생 하므로 long에 기록)
- 2^31 - 1 bytes의 경우 약 2GB 이므로 이 정도의 채팅 데이터가 쌓이게 되면 index 파일을 하나 더 생성 하는 식으로도 처리 가능
(2) TextElement
- userType, sender, data, timeline, createAt으로 구성
- Index와 달리 한 블록의 크기가 가변적
- 특정 줄을 찾기 위해 각 줄을 읽어 찾으려면 굉장히 비효율적임
- Index 파일 블록에서 읽어온 file offset을 이용해 특정 데이터로 Seek 가능
- 파일에 매번 데이터를 입출력 할 경우 File Open/Close 가 반복되므로 성능 저하 발생
- IndexElement 의 경우 ByteBuffer, TextElement의 경우 CharBuffer를 이용해 데이터를 버퍼에 쌓고, 버퍼가 가득 차면 파일에 flush 하여 성능 개선
- 아래의 Split / Encoding 작업 전 버퍼에 데이터가 남아있는지 체크하여 잔여 데이터 flush
(3) Split
- .txt, .idx, .m3u8 파일을 이용해 .txt 파일을 분할
- HLS와 비슷하게 .m3u8 파일을 읽어 세그먼트의 크기를 결정함
- 채팅 timeline값이 단조증가하는 형태로 기록되므로 이분 탐색 가능
- 구간 별로 이분 탐색 Lower bound / upper bound 를 이용해 자를 구간 선정
타임라인 값은 중복 된 값이 존재할 수 있으므로 그냥 이분 탐색으로는 정확한 위치를 찾기 힘듬- ex) timeline 값이 12인 (방송 시작 12초 경과) 시점에 여러 채팅이 존재할 수 있음
- Lower Bound는 해당 값이 들어갈 수 있는 하한선, Upper Bound는 해당 값이 들어갈 수 있는 상한선
- 자르려는 왼쪽 값의 하한선과 자르려는 오른쪽 값의 상한선을 찾는 것
(4) Encoding
- HTTP2의 경우 헤더 크기를 줄이기 위해 Huffman Encoding을 이용하여 헤더를 압축함
- 이와 비슷하게 잘린 채팅 세그먼트 크기를 줄이기 위해 허프만 압축을 이용하여 압축
- Huffman Encoding
- 데이터 Read → 글자 수 별 빈도수 계산 → 허프만 트리 구성 및 Leaf 노드 구성 → Leaf 노드 별 허프만 코드 할당 및 데이터 인코딩
- 빈도 수가 많은 글자에는 짧은 이진 문자열을, 빈도수가 적은 문자에는 긴 이진 문자열을 할당 (그리디 알고리즘 성향)
- Huffman Decoding
- 파일 앞에 쓰여진 디코딩 헤더 정보 (허프만 트리를 재구성 하기 위한 정보) 를 읽어 파일 원복
- 전체 글자 종류, 허프만 트리 정보, 인코딩된 데이터로 구성됨
(5) Result
- Split & Encoding 을 통해 각 txt 파일 세그먼트를 ch 파일로 변환
- 이후 클라이언트에서 m3u8 다운로드 후 구간에 맞는 ch 파일 요청, 디코딩 작업을 통해 채팅 데이터를 읽음
- m3u8을 읽어와 ch 파일을 다운받고 디코딩 하여 txt 파일을 생성
- txt 파일에서 채팅 데이터를 읽어 적절한 값을 출력
- 시간이 경과됨에 따라 다음 채팅 세그먼트를 다운 받아 같은 방식으로 처리함
- 로컬에 해당 세그먼트의 파일이 이미 존재하면 로컬 파일을 읽어옴
- timeline 바를 이용해 특정 시점으로 시간을 Seek 할 경우
해당 구간에 맞는 채팅 세그먼트를 읽어와 처리 후 출력함을 확인할 수 있습니다. - 아래의 Split / Encoding 작업 전 버퍼에 데이터가 남아있는지 체크하여 잔여 데이터 flush