Slide 1

Slide 1 text

Cloud Spanner 開発・運用で得られたノウハウ 1 山田 和毅

Slide 2

Slide 2 text

自己紹介 ● 山田 和毅 ● 株式会社コロプラ 2017年新卒入社 ● ユージェネチーム 2

Slide 3

Slide 3 text

じめに ● Spanner ○ スケーラブルで安定性も高くコロプラ 多く タイトルで採用 ○ フルマネージドで挙動が不明な部分もある ● 開発・運用する上で得られたノウハウを共有 ○ MySQL から Spanner へ 移行による開発 変化 ○ 開発・運用で気をつけること 3

Slide 4

Slide 4 text

1. Spanner 移行による開発 変化 2. 開発・運用で気をつけるべきこと 4 4

Slide 5

Slide 5 text

ユーザーデータ 取り扱い ● MySQL ○ シャーディングで負荷分散 ○ 処理によって 分散トランザクションが必要 ● Spanner ○ インターリーブ ■ テーブルに親子関係(例: User => UserItem) ■ 物理的に同じ場所に配置される で高パフォーマンス ○ ユーザー間を横断してスキャンなど 苦手 5

Slide 6

Slide 6 text

シャーディングから 解放 ● 自前 シャーディング(MySQL) ○ 接続先をコードで指定 ■ 途中で分割数を変えていたりするとさらに辛い ○ 調査 難易度が高い ■ コロプラ Nite という内製ツールでカバー ● 水平/垂直分割されたRDBMSを便利に検索するため ツールを内製した話 ● シャーディング 考慮がほぼ不要 ○ アプリレイヤ コードが簡潔に ○ ホットスポット回避で分散用 カラムを追加すること ある 6

Slide 7

Slide 7 text

レプリケーション 変化 ● アクセス先やレプリケーション遅延 考慮不要 ● 注意事項 ○ 調査クエリがサービスに直接影響 ■ ログを多めに残しておき BigQuery を使用 ■ 別インスタンスにデータを複製しそ 中でクエリを実行 ○ 管理ツール 実装にも Spanner へ 負荷に注意が必要 ■ コロプラ 横断検索に Elasticsearch などを使用 ● ユーザー名、ランク、ログイン日時など 7

Slide 8

Slide 8 text

共通データ(マスターデータ) 取り扱い ● マスターデータと ○ コロプラで ユーザーが共通で使うデータを別DBで管理 ○ 武器・アイテム・キャラクターなど データ ● 変わらず MySQL を使用 ○ Spanner 同一レコードへ 参照 ホットスポットになりやすい ■ 例:キャッシュがないタイミングで   複数ユーザーが特定 アイテムID データを取得 ○ 一部 タイトル MySQL互換 TiDB を使用 8

Slide 9

Slide 9 text

カラム追加/変更が容易 ● MySQL ○ オンライン ALTER ○ JSON カラムを作って値を管理など ● Spanner ○ レコード量に関係なく自由に変更可能 ■ 適用に時間 かかる ■ 複数カラム 更新なども特に問題なく可能 ■ コロプラで Grafana などで負荷を監視しつつ実行 ○ カラム追加時に NOT NULL が指定不可という ある 9

Slide 10

Slide 10 text

1. Spanner 移行による開発 変化 2. 開発・運用で気をつけるべきこと 10 10

Slide 11

Slide 11 text

クエリパラメータ数 上限 ● クエリパラメータ ○ SELECT … WHERE IN (@a, @b, @c) @a など ○ 950個を超えるとエラー ● ミューテーション ○ トランザクション内で 20000 を超えるとエラー ■ 列数 + インデックス ある列数 ■ (参照: mutation 数え方) ○ バッチ処理など一気にデータ入れる場合など 気をつける 11

Slide 12

Slide 12 text

クエリパラメータ数 上限 ● 実際にリリース前に修正したも ○ ユーザー 図鑑データを図鑑IDで WHERE IN ○ 位置系データ ユーザーデータを WHERE IN ● 対応 WHERE IN をやめるか分割して取得するなど ● WHERE IN しているコードやクエリログ 要チェック ○ マスターデータを元に WHERE IN している箇所 特に注意 ■ 運用でデータが増える可能性あるため常に気を遣うように 12

Slide 13

Slide 13 text

トランザクション リトライ ● Spanner 性質上トランザクション リトライが起きやすい ○ リトライされる前提でコードを書く ● Spanner 以外 データ更新を常に注意 ○ static変数 ○ Redis など キャッシュ ○ キュー ○ イベント発行 ● static変数使わない、ロールバックなど 処理を正しく書く 13

Slide 14

Slide 14 text

トランザクション内 SELECT 空振り ● 「SELECT してデータがなけれ INSERT」 パターン ● データがない場合に abort する確率が上がる ○ リトライが発生し負荷も上がる ● 空振り前提 場合 SELECT せず INSERT するほうが良い ○ UUID 被り チェック しない ○ 被ったらエラーでリトライ 14

Slide 15

Slide 15 text

インデックス 必要な時だけ付ける ● インデックス 内部的に テーブル ● 必要なときだけ付ける ○ Google 内 テストだと、SELECT した結果が テーブル全体 5% 以内 つけると速くなるという検証結果 ■ (参照: D2-5-S06: Cloud Spanner in Action - YouTube ) 15

Slide 16

Slide 16 text

Query Optimizer バージョンを固定 ● 何もしないと自動で変わる ○ v2: 2020/3/1 ○ v3: 2021/8/1 ● GCPコンソールに お知らせが出る ● バージョンを固定しておくと安心 ○ 任意 タイミングでバージョン変更対応を展開 ○ ALTER DATABASE MyDatabase SET OPTIONS (optimizer_version = 3); ○ (参照: Managing the query optimizer | Cloud Spanner) 16

Slide 17

Slide 17 text

インターリーブ内が 8GB を超える場合 ● インターリーブ内 データ 基本1つ スプリット ● 8GB を超えるとスプリットが分割 ○ スプリットを跨いだ参照になりうる ○ サイズが見れないため増えていることに気づきづらい ○ (参照: Schema and data model | Cloud Spanner) ● 単調増加するデータ 入れず削除できるも 削除 ○ 物理削除な でアプリレイヤでログを慎重に残すことが重要 ○ コロプラで 一部 API で◯%だけ削除するなどをやっている ○ TTL を使用することで定期的に削除するポリシーを設定可能 17

Slide 18

Slide 18 text

リリース前に必ず見ておくポイント ● クエリパラメータ 制限に引っかからないか ● トランザクション リトライされても大丈夫か ● SELECT 空振りコード確認 ● 無駄にインデックス貼っていないか ● ホットスポットにならないか ● 高頻度に単調増加するログなどを入れていないか ○ インターリーブ内 データが 8GB を超えないか 18

Slide 19

Slide 19 text

まとめ ● Spanner 導入による開発 変化 ○ ユーザーデータ取り扱い 大きな変化 ○ マスターデータ 非Spanner ● 気をつけること ○ クエリパラメータ、ミューテーション 上限 ○ トランザクション リトライ考慮したコード ○ そ 他 リリース前 チェックポイント 19

Slide 20

Slide 20 text

補足資料 21

Slide 21

Slide 21 text

ステイル読み取り Stale Read ● 古いデータ(10秒以上前)を許容する場合に使用 ○ パフォーマンス向上が見込める ○ 他ユーザー名 取得など ● 強力な読み取り Strong Read ○ データが最新 状態か 確認が入り遅くなる場合がある ■ レプリカからリーダーへ 通信 22

Slide 22

Slide 22 text

ローカル開発で エミュレータが便利 ● Spanner ローカル開発がしづらい ○ 常にSpannerに通信できる環境である必要がある ○ データベース 作り直し処理などに少し時間がかかる ● 公式 エミュレーター ○ ローカル メモリ上で動く で快適 ○ デメリット データを永続化できないこと 23

Slide 23

Slide 23 text

GCPコンソール上で 注意点 ● トランザクションが貼れない ○ UPDATE文などを間違えられない ● 時刻 UTCで扱われる ○ 内部 UTCで保存され表示もすべてUTC ○ 9時間 時差を考慮 ■ x: SELECT ... WHERE createdAt > "2022-02-16 19:30:00" ● これだと午前3時になる。まだ勉強会始まってない ■ o: SELECT ... WHERE createdAt > "2022-02-16 19:30:00+09:00" 24