Upgrade to Pro — share decks privately, control downloads, hide ads and more …

奥深きキャッシュの世界 / The world of profound cache

206cd98801e9ad1a1a246da5f3b5a40b?s=47 ykoma
November 29, 2018

奥深きキャッシュの世界 / The world of profound cache

2018年11月29日の社内勉強会発表資料

アジェンダ
- キャッシュのおさらい
- キャッシュの実装パターン
- 起こりがちな問題とその対策
- Thundering Herd問題の解決アプローチ
- まとめ

206cd98801e9ad1a1a246da5f3b5a40b?s=128

ykoma

November 29, 2018
Tweet

Transcript

  1. 奥深きキャッシュの世界 2018-11-29

  2. アジェンダ 1. キャッシュのおさらい 2. キャッシュの実装パターン 3. 起こりがちな問題とその対策 4. Thundering Herd問題の解決アプローチ

    5. まとめ 2
  3. 駒原 雄祐 サイバーエージェント ソフトウェアエンジニア Media / Advertisement / Scala /

    golang / AWS etc... 誰? 3
  4. キャッシュのおさらい 1 4

  5. キャッシュってどういうもの? ◉ データソースの過負荷からの保護や低レイテンシの維 持のために ◉ データ利用者(アプリケーション等)の近くに配置された ◉ 高速な読み書きが可能な一時保存用のストア領域のこ と ◉

    もしくはそのようなストア領域にデータソースから受けっ たデータを一時的に保持すること 5
  6. 一言で言えば データソースへのアクセス頻度を 下げる仕組み 6

  7. キャッシュあれこれ ◉ メモリに対する、CPUのキャッシュ ◉ 閲覧済みデータを保持するブラウザキャッシュ ◉ スマホアプリの使用データのキャッシュ ◉ Web+DBシステムにおけるDBデータのキャッシュ 7

  8. キャッシュあれこれ ◉ メモリに対する、CPUのキャッシュ ◉ 閲覧済みデータを保持するブラウザキャッシュ ◉ スマホアプリの使用データのキャッシュ ◉ Web+DBシステムにおけるDBデータのキャッシュ 8

  9. “ Web+DBシステムで よくある構成 9

  10. よくある構成 10 Web App Server Web App Server Web App

    Server RDB Load Balancer Internet Traffic
  11. よくある障害 11 アクセス過多による 負荷高騰 →スローダウン Web App Server Web App

    Server Web App Server RDB Load Balancer Internet Traffic
  12. よくある障害 12 そのままサービス全 体が障害に Web App Server Web App Server

    Web App Server RDB Load Balancer Internet Traffic
  13. “ キャッシュを配置 13

  14. キャッシュを配置 14 Web App Server Web App Server Web App

    Server RDB Load Balancer Internet Traffic
  15. キャッシュを配置 Cache 15 RDBの前段にキャッ シュレイヤーを配置 RDBはキャッシュを すり抜けてくる問合せ だけを処理 Web App

    Server Web App Server Web App Server RDB Load Balancer Internet Traffic
  16. キャッシュの 実装パターン 2 16

  17. 実装パターン ◉ Primed Cache ◉ Demand Cache ◉ (観点は異なるが) Local

    Caching 17
  18. “ Primed Cache 18

  19. Primed Cache 19 Data Source Client 使用するデータを予め キャッシュにロード Cache

  20. Primed Cache 20 Data Source Client クライアントはキャッシュにの み問合せを行う Cache

  21. Primed Cache 21 Data Source Client ◉ パフォーマンスのばらつきが少なく、常に高速なレスポン スが可能 ◉

    適用できるユースケースが限られる ◦ 使用されるデータセットが予測可能であること ◦ 予測から漏れたデータはたとえデータソースにあって もクライアントは取得できない Cache
  22. “ Demand Cache 22

  23. Demand Cache (1) 23 Data Source Client ②Respond data in

    cache キャッシュからの取得を試み、 キャッシュにあればそれを使用 Cache ①Try to read from cache
  24. Demand Cache (2) 24 Data Source Client ①Try to read

    from cache ②Read from data source if cache missed キャッシュになかった場合、 データソースに問合せる Cache
  25. ③Store requested data to cache Demand Cache (2) 25 Data

    Source Client ①Try to read from cache ④Respond data from data source データソースからのデータを返す とともに、キャッシュにデータを保 存し、次回以降の問合せに備え る Cache ②Read from data source if cache missed
  26. ③Store requested data to cache Demand Cache (2) 26 Data

    Source Client ①Try to read from cache ④Respond data from data source ◉ 使用されるデータの予測ができないユースケースでも適用可能 ◉ ヒット率等の要因でパフォーマンスにばらつきが出る事がある Cache ②Read from data source if cache missed
  27. “ Local Caching 27

  28. Local Caching ◉ クライアントのオンメモリでキャッシュを持つ ◦ アプリケーションプロセス内のメモリや、同一ホスト内のイ ンメモリストアなど ◦ ランタイム標準のmap等に加え、各言語インメモリキャッ シュの実装も多数ある

    28
  29. Local Caching ◉ 主なユースケース ◦ 外部キャッシュの負荷が大きく、すべての問合せをさばききれな い場合 ◦ 外部キャッシュとの通信のレイテンシが許容できない場合 ◦

    何らかの理由でデータソースの前段にキャッシュレイヤーを置く ことができない場合 29
  30. 30 Client Cache Local Caching ◉ 主なユースケース ◦ 外部キャッシュの負荷が大きく、すべての問合せをさばき きれない

    ◦ 外部キャッシュとの通信のレイテンシが許容できない ◦ 何らかの理由でデータソースの前段にキャッシュレイヤー を置くことができない
  31. 31 Client Outside Cache Local Caching ①Try to read from

    on-memory cache ②Try to read from outside cache if on-memory cache missed Demand Cacheにおけるキャッシュとデータ ソース間と同じ処理を、クライアント上のメモリ とキャッシュ間でも行う On-memory Cache
  32. ④Store requested data to cache Demand Cache with Local Caching

    32 Data Source Client ②Try to read from outside cache ③Read from data source ⑤Respond data from data source ◉ このように組み合わせて使うことも多い ◦ データソースとキャッシュ間に差異が生まれやすいため、キャッ シュデータのライフサイクル管理が重要!! Outside Cache ①Try to read from on-memory cache ⑥Store requested data to on-memory cache On-memory Cache
  33. 起こりがちな問題と その対策 3 33

  34. “ キャッシュの容量不足により Evictionが多発 34

  35. Evictionとは ◉ 容量が溢れた時に、所定のルール(アルゴリズム)に従って データを追い出す機能のこと ◦ アルゴリズムもいろいろある(LRU,LFU,FIFOなど) ◉ Evictionの発生が即問題なわけではない ◦ が、Evictionが多発している状況だとキャッシュのヒット率に

    も影響してくる ◉ EvictionがOFFの状態で容量オーバーになると、それ以上 データが書き込めなくなるなどより深刻な事態に 35
  36. 何が起きた? ◉ 想定と実際のユースケースとの乖離 ◦ 想定よりも実際のデータ量が多かった ◦ 想定よりも実際のデータサイズが大きかった ◉ 想定以上にキャッシュの消費量が多かった ◦

    エンコーディング(どういう形式で格納されるか) ◦ ブロックサイズ(どういう単位で格納されるか) ◉ モニタリングができていない ◦ 容量が溢れそうなことを事前に検知すべき 36
  37. 対策 ◉ キャパプラちゃんとしよう ◦ 想定データをある程度のデータ量実際に入れてみて 測定するのが一番確実 ◉ モニタリングちゃんとしよう ◦ 容量の監視は基本中の基本

    ◉ 水平分散可能なキャッシュ設計にして足りなく なったら足せるようにしよう 37
  38. “ 水平分散しろって言うから 水平分散してたのに キャッシュノードを増設したら 障害になったんだけど (怒) 38

  39. 何が起きた? ◉ memcachedなどノード分散がクライアントに委ねられ ている場合に起こりがち ◉ キーから格納ノードの解決(分散アルゴリズム)が適 切でないことが原因 ◦ 多くのキーのマッピング先が変わることで一時的に ヒット率が大幅に下がり、レイテンシの悪化やデータ

    ソースの過負荷につながった ◉ 増設時だけでなく、ノード障害等でキャッシュノードが 減ったときにも起こる 39
  40. 対策 ◉ 分散アルゴリズムにConsistent Hashingを採用する ◦ キャッシュノード増減時のキー→ノードのマッピン グの変更を最小限に 40 https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%82%B7%E3%82% B9%E3%83%86%E3%83%B3%E3%83%88%E3%83%8F%E3%83%83%E3%8

    2%B7%E3%83%A5%E6%B3%95 (参考) コンシステントハッシュ法 - Wikipedia
  41. “ アプリケーションの プロセス起動直後が遅いよー 41

  42. 何が起きた? ◉ Local Cachingをしている場合に起こりがち ◉ リリースによるプロセス再起動や、オートスケーリン グによるスケールアウトの直後などに起こる ◉ プロセス起動直後はローカルキャッシュが空っぽの 状態

    ◦ すべての問合せに対して外部にデータを取りに 行ってしまう 42
  43. 対策 ◉ 対策1: ローカルキャッシュのウォームアップ ◦ 起動後、待受ポートを開く前に頻出データを先読み ◦ ただし、頻出データが分かっている前提 ◉ 対策2:

    ローカルキャッシュを一部Primed Cache方式 に ◦ 件数が少なくて参照頻度の高いマスタデータなどは、常 に全件をローカルキャッシュに持っておくのも一つの手 43
  44. “ 分散キャッシュのうち 一部のノードが遅い or/and 負荷が高い 44

  45. 何が起きた? ◉ Local Cachingなし+分散キャッシュの組み合わせで 起こりがち ◉ 特定のキーに対する過剰なキャッシュへの問合せ ◉ 結果的にそのキーを持っているキャッシュノードにア クセスが集中

    ◉ 分散キャッシュだけでなく分散ストレージでもよくある ◉ 「ホットスポット問題」と呼ばれたりする 45
  46. 対策 ◉ 対策1: キーに一定の範囲内の乱数を組み合わせ、同 一キーを複数ノードに保持する ◦ 参照時も乱数を生成してそのどれかを抽選することで 参照ノードがバラける ◦ どの程度の数を生成するかが重要な課題に

    ▪ 生成数が多いほどヒット率が下がる ▪ 生成数が不十分だと格納先が重複して偏りが解消 されない ▪ 件数の多いデータだとキャッシュ容量圧迫も ◉ 対策2: Local Cachingを導入する 46
  47. “ キャッシュしてるのに 突然大量の問合せが データソースに来始めちゃった・・ 47

  48. 何が起きた? ◉ 存在しない(もしくは無効な)データへの問合せが大量 に発生している ◦ 問合せ時にデータが見つからないため、キャッ シュに載らない ◦ そのため、以降発生する同じデータへの問合せ もすべて空振り

    ◦ 結果、データソースまで大量の問合せが到達 ◉ 頻出マスタデータの削除など ◉ 急に発生したりするので、障害に陥りやすい 48
  49. 対策 ◉ 「該当データがなかった」という結果をキャッシュする ◦ このようなキャッシュの事を「ネガティブキャッシュ」 と呼ぶ (DNSなどでは一般的) 49

  50. “ 事前の想定よりもヒット率が低い or/and 一定間隔でヒット率が 下がる 訴えてやる! 50

  51. 何が起きた? ◉ 非常に高頻度なアクセス状況で起きやすい ◉ キャッシュ(ローカルキャッシュ含む)で保持するデータ の有効期限切れ(Expiry)や明示的な削除がトリガ ◉ 次にキャッシュがロードされるまでの間、大量の問合 せがデータソースまで到達してしまう ◉

    低速なデータソースであるほど影響が大きい ◉ 「Thundering Herd問題」や「Cache Stampede問題」 と呼ばれたりする 51
  52. 図解(Demand Cacheの流れ) 52 get(A) return valueOf(A) get(A) return valueOf(A) get(A)

    return valueOf(A) set(A) get(A) return valueOf(A) キャッシュの生 存期間 Data Source (key=A) Cache (key=A) Client
  53. 図解(Demand Cacheの流れ) 53 get(A) return valueOf(A) get(A) return valueOf(A) get(A)

    return valueOf(A) set(A) get(A) return valueOf(A) Data Source (key=A) Cache (key=A) Client このスキマ時間に 悲劇は起こる
  54. 図解(高トラフィックな状態) 54 キャッシュに乗るまで全 部のget(A)がデータソー スまで・・・ Data Source (key=A) Cache (key=A)

    Client get(A) return valueOf(A) get(A) return valueOf(A) get(A) return valueOf(A) set(A) get(A) return valueOf(A)
  55. 図解(Local Cachingでも同様) 55 Outside Cache (key=A) Local Cache (key=A) Client

    get(A) return valueOf(A) get(A) return valueOf(A) get(A) return valueOf(A) set(A) get(A) return valueOf(A)
  56. Thundering Herd問題の 解決アプローチ 4 56

  57. “ Semaphore 57

  58. Semaphore (セマフォ) ◉ セマフォとは ◦ 対象リソースに対する現時点で利用可能な数を 管理する仕組み ◦ セマフォを使うことで「同時並行可能な最大数を 超えないように制御」できる

    58
  59. Data Source (key=A) Cache (key=A) Client セマフォを使わない場合 59 get(A) set(A)

  60. Data Source (key=A) Cache (key=A) Client セマフォ(同時利用可能数2) を使った場合 60 get(A)

    set(A) セマフォが0になっ たので以降の問合 せは空きが出るま で待つ 最大でも同時 利用可能数の 問合せ get(A) セマフォに空きが出 たので待っていた問 合せをする
  61. 参考 61 ◉ Java ◦ java.util.concurrent.Semaphore https://docs.oracle.com/en/java/javase/11/docs/api/java. base/java/util/concurrent/Semaphore.html ◉ Golang

    ◦ golang.org/x/sync/semaphore https://godoc.org/golang.org/x/sync/semaphore
  62. “ Singleflight (for golang) 62

  63. singleflight (for golang) 63 ◉ 同時利用可能数1のセマフォに近い ◉ 空きが出るまで待つのではなく、同時実行中の最初 の実行された結果を他の問合せ結果に使い回す ◉

    Golangでは拡張ライブラリに組み込まれている ◦ golang.org/x/sync/singleflight https://godoc.org/golang.org/x/sync/singleflight
  64. Data Source (key=A) Cache (key=A) Client singleflight (for golang) 64

    get(A) 最初の1本以外は最 初の1本の結果を待 つ データソースに 到達するのは 最初の1本だ け 待っていた他の問 合せに対しても、1 本目と同じ結果を返 す set(A)
  65. “ Asynchronously refresh 65

  66. Asynchronously refresh (非同期リフレッシュ) 66 ◉ キャッシュがない状態での問合せに対しては、同期 的にデータソースにアクセスしてキャッシュに載せる ◉ それ以降は、対象データに問合せがあるうちは、一 定間隔で非同期にキャッシュを更新する

    ◉ 一定期間以上問合せがなければキャッシュをクリア (有効期限切れ)する ◉ 参考 : Caffeine Cache (Javaでの実装) https://github.com/ben-manes/caffeine/wiki/Refresh
  67. Data Source (key=A) Cache (key=A) Client Asynchronously refresh 67 get(A)

    set(A) get(A) キャッシュがない状 態からは同期的に 取得してキャッシュ へ キャッシュが 載ってからは 非同期に更新 問合せがある間は キャッシュが切れな い
  68. まとめ 5 68

  69. 「キャッシュ」と 一口に言っても ◉ いろんな実装方法がある ◉ いろんな障害パターンがある ◉ それに対する対策もたくさん 69

  70. 最後に ◉ キャッシュ構築の「基本形」にはお決まりのパターン がある ◦ 一方、思わぬところで足をすくわれるのが「キャッシュ」 という領域 ◦ いざ障害になると長期化することも多く、決して甘く見 てはいけない

    ◦ 特にWeb系の人は割と軽いノリで「キャッシュ入れよ う」となりがち?(先入観) ◉ 考慮すべき点はきちんと考慮して、頑強なキャッシュ を構築しましょう 70
  71. Have a happy caching life!! 71

  72. ご清聴ありがとうございましたm(_ _)m 72