何を索引し、何を本文に残すか
目標
ログストレージが遅くなったり高くなったりする理由は、ほとんどの場合何をインデックス化するかの選択を誤ったからです。その判断は、データがたまったあとでは元に戻すのが非常に困難です。
ここでは実際のログを数えて、数字で決めます。
用意するもの
/opt/lab/logidx/events.jsonl 로그 2만 줄 (약 4MB)
mkdir -p /root/logidx && cp /opt/lab/logidx/* /root/logidx/ && cd /root/logidx
head -1 events.jsonl | python3 -m json.tool
残すもの
01-cardinality.txt 필드마다 값이 몇 가지인가
02-labels.txt 라벨과 본문을 가른 결과
03-explode.txt 라벨 하나를 더하면 스트림이 몇 개가 되나
04-shards.txt 샤드 수
05-tiers.txt 보관 단계와 기간
06-cost.txt 색인과 검색의 비용
07-notes.md 왜 그런지
まず数えてみる
各フィールド(service level env pod route request_id user_id)が異なる値をいくつ持つかを数えて01-cardinality.txtに書き、多いものと少ないものを分けて示してください。
python3 - <<'PY'
import json
rows = [json.loads(l) for l in open('events.jsonl')]
for k in ('service','level','env','pod','route','request_id','user_id'):
print(k, len({r[k] for r in rows}))
PY
2桁以下と万単位が混在しているはずです。その差がそのままコストの差になります。
ラベルと本文を分ける
ラベルにするものと本文に置くものを分けて02-labels.txtに書いてください。ラベルとして選んだものの組み合わせ数も一緒に計算します。
ラベルはそのままストリーム(または時系列)です。組み合わせ数がそのまま個数になります。
service・level・envの3つなら5 × 3 × 2ですが、実際に現れる組み合わせだけを数えると、それより小さくなることもあります。実際のデータで数えてみてください。
カーディナリティの高いフィールドは本文に置きます。そうすればラベルで先に絞り込んでからその中を探すことになり、それがこの種のストレージの設計意図です。
1つだけ足してみる
ラベル3つにpod・route・request_id・user_idを1つずつ加えたとき、組み合わせ数がいくつになるかを03-explode.txtに書き、pod1つで何倍になるかも書いてください。
4回計算して比べます。podはサービスごとに6つあるので掛け算されます。
「このラベルが1つあれば便利なのに」という考えが事故の始まりです。便利さは1人のもので、コストはクラスター全体のものです。
シャードをいくつに分けるか
このログが毎秒500行入ってくるものとして、1日分の保存量とシャード数を04-shards.txtに計算してください。目標のシャードサイズを決め、1日分がそれより小さい場合にどうするかも書きます。
1行のサイズは、ファイルサイズ ÷ 行数で求めます。
シャード1つあたり10–50GBを目標にします。小さなシャードが数千個あると、その分メモリとファイルハンドルを消費し、クラスターが遅くなります。
1日分が目標より小さければ、日別の代わりに週別にするか、ロールオーバー条件(サイズ基準)に任せます。
いつ何を捨てるか
hot・warm・cold・deleteの4段階の期間を決め、各段階にどれだけたまるかを05-tiers.txtに計算してください。warmやcoldで何が変わるかも書きます。
hotは書き込みと検索の両方が行われ、warmは読み取り専用なのでセグメントをマージしてレプリカを減らせます。coldは遅いストレージやオブジェクトストレージへ移します。
決めておかないと、ある日ディスクがいっぱいになり、そのときは慌てて削除するあまり、必要なものまで消してしまいます。
コストはどこで発生するか
インデックス化側のコストと検索側のコストを分けて06-cost.txtに書き、インデックス化のコストを減らす方法と検索しないフィールドの扱い方を一緒に書いてください。
毎秒のドキュメント数がそのままCPUです。デバッグレベルのログを本番でそのまま送ると、そのコストを毎日支払うことになります。
検索はせず表示するだけのフィールドはindex: falseにしておくと、インデックス化のコストと保存容量が減ります。逆に、集計だけに使う数値はdoc_valuesがあれば十分です。
正常なリクエストはトレースに任せ、ログは異常な場合にだけ残す方向へ移すのも1つの方法です。
次に読む人へ
ここで見たものの中から4つ以上を選んで07-notes.mdにまとめてください。何をしたかではなく、なぜそうなのかを書きます。
新しいフィールドをラベルに入れるか悩んでいる自分が読むと考えてください。「カーディナリティを数えた」は役に立たず、「podをラベルに入れるとストリームが6倍になります。便利さは1人のもので、コストはクラスター全体のものです」は役に立ちます。