Netflixが直面した問題がある:クライアントデバイス上のトークンがどんどん大きくなっていた。
各広告にはトラッキングURL、メタデータ、ベンダー情報が必要だった。パートナーが増えるにつれ、トークンは太くなった。ローエンドのTVはメモリで苦しみ始めた。
解決策は?間接参照。
トークンにすべての広告メタデータを入れていた—複数のベンダートラッキングURL、タイトルやdurationなどの完全なメタデータ、大量のベンダー固有データ。2KBが各広告と一緒に送られていた。
代わりにやったこと?2つのフィールドだけ送る:adIdとmetadataRef。50バイト。実際のデータはレジストリに保存して、クライアントが必要なときにルックアップする。
トークンは2KBから50バイトに。ローエンドTVも満足。今の帯域を後のルックアップとトレードする—たいてい価値がある。
メッセージキューでは、大きなペイロードをKafkaに入れるな。S3への参照を入れろ:
データベース設計では、埋め込む代わりにIDを保存してジョインしろ:
キャッシングはそもそも間接参照だ。小さなキーが大きな値を指す—cache.get("user:123")は8バイトのキーから完全な50KBのユーザーオブジェクトを返す。
短縮URLも間接参照だ。bit.ly/abc123がパラメータ付きの100文字のURLを指す。
クライアントデバイスのリソースが限られているとき、メッセージサイズが爆発しているとき、ネットワーク帯域が貴重なとき、すべてを再送せずにデータを更新する必要があるとき。
トレードオフとして、帯域とレイテンシを交換する。直接アプローチは高帯域・低レイテンシ・シンプル・密結合で更新には全部再送が必要。間接参照アプローチは低帯域だがルックアップ1回分のレイテンシがあり、可動部品が増え、疎結合でレジストリを更新するだけでいい。
データが大きくて頻繁に変わるなら、たいてい間接参照の価値がある。
「コンピュータサイエンスのすべての問題は、間接参照のもう1つのレベルで解決できる。」
そしてその系:
「...間接参照のレベルが多すぎるという問題を除いて。」
追加すべきときを知れ。やめるべきときを知れ。
1レベルの間接参照は問題を解決する。2レベルは新しい問題を作る。
— blanho
{
"adId": "123",
"metadataRef": "abc-456"
}# 悪い例: Kafkaに10MBのペイロード
producer.send("topic", large_image_bytes)
# 良い例: S3への参照
producer.send("topic", {"s3_key": "images/abc123.jpg"})-- IDでジョイン(非正規化ブロブより良い)
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE c.name = 'John'