1 of 61

GCP Database Options

GCPUG Admin

Google Developers Expert

Mercari / Merpay Solution Team

@sinmetal

https://gcpug.jp

2 of 61

GCP Database Service

  • Cloud Bigtable
  • Cloud Datastore
  • Firebase Realtime DB (Googleが買収したサービス)
  • Cloud Spanner
  • Cloud Firestore
  • Cloud SQL
  • Cloud Memorystore
  • BigQuery (DBと呼ぶかはあれだけど、DBっぽく使うことが有るので)

2

太字はGoogleが独自に開発したDB

https://gcpug.jp

3 of 61

GCP Databaseの系譜 (sinmetalの主観)

3

Bigtableをアプリケーションで使いやすいようにQueryやTxを追加し、Bigtableのインフラを共有することでコストを抑えた

Datastoreよりパフォーマンスを改善し、大量のトラフィックを高速に捌けるようになった。

RDBのような柔軟なQueryにも対応

Firebase Realtime DB, Datastore, Spannerのノウハウを融合し、よりGCPらしいRealtime DBへ

Bigtable

Datastore

Spanner

Realtime DB

Firestore

https://gcpug.jp

4 of 61

Cloud Bigtable Introduction

  • 分散型KVSのDB
  • ストレージは自動的に拡張されていくので、データを延々と入れていくことができる
  • クエリのような機能はなく、KeyでのGetのみ
  • Transaction機能がないので、センサーデータのような追記し続けるデータに向いている
  • 課金体系はNode数とストレージ容量

4

https://gcpug.jp

5 of 61

Cloud Bigtableの後ろ側

5

DataはTabletに保存される。

Tabletは動的に増減する。

Tablet

Tablet

Tablet

Tablet

Node

Node

Client

https://gcpug.jp

6 of 61

Tabletの決定方法

  • Bigtableは行Keyの辞書順にデータを保存する
    • PKの先頭付近の文字が重要

6

Tablet

Tablet

Tablet

Tablet

a ~ e

f ~ j

k ~ o

p ~ z

今、Tabletが管理しているKeyの範囲を管理しておけば、指定されたKeyのRowを持っている可能性があるTabletが分かり、そのTabletを担当しているNodeが分かる

Node

Node

Client

https://gcpug.jp

7 of 61

Tabletの分割

  • 1つのTabletが大きくなりすぎたり、負荷が高くなりすぎると、Tabletを分割する

7

Tablet

Tablet

Tablet

Tablet

a

f ~ j

k ~ o

p ~ z

Node

Node

Client

Tablet

b ~ e

a のPKのデータばっかり増えたので、a ~ eを分割

https://gcpug.jp

8 of 61

Tabletの再配置

  • Nodeの増減時はTabletの割当が再配置される

8

Tablet

Tablet

Tablet

Tablet

a

f ~ j

k ~ o

p ~ z

Node

Node

Client

Tablet

b ~ e

Node

https://gcpug.jp

9 of 61

Tabletのhotspot問題

  • 行Keyに偏りがあると、Tabletにhotspotができてしまう

9

Tablet

Tablet

Tablet

Tablet

aa ~ af

f ~ j

k ~ o

p ~ z

Node

Node

Client

Tablet

b ~ e

Tablet

ag ~ az

hot

spot

hot

spot

hot

spot

ひたすらaの行Keyのデータが増えていく

https://gcpug.jp

10 of 61

行Keyの戦略

  • 行Keyはなるべく、分散するようにしておく
    • ただし、Bigtableは行Keyの範囲をスキャンすることしかできないので、クエリのことも考える必要がある
  • バッドパターン
    • シーケンシャルナンバー
    • タイムスタンプ

10

https://gcpug.jp

11 of 61

行Keyの戦略

11

10,20,30,40,50

INSERT 10, 20, 30, 40, 50

10,20

30,40,50

INSERT 60,70,80,90

30,40,50, 60, 70, 80 ,90

10,20

30,40,50

60,70,80,90

10,20

分割

分割

後ろのTabletにINSERTが偏る

https://gcpug.jp

12 of 61

行Keyの戦略

12

30,40,50

60,70,80,90

10,20

INSERT 15,23,35,67

30,35,40,50

60,67,70,80,90

10,15,20,23

データが増えていく時に別々のTabletに書き込まれるようにしていく

データがない状態だと、分散していない状態からスタートするので、じわじわデータを入れていく

Cloud Datastoreのベストプラクティス "500/50/5" rule が参考になる

ローンチした瞬間に凄まじいトラフィックが予想される場合は、ダミーデータを入れておくなども有効

NOTE : “500/50/5” rule

新しいKindに対するオペレーションは毎秒 500 回を上限とし、その後でトラフィックを50%ずつ、5 分ごとに増やしていく

理論上は、この増加スケジュールを使用すると 90 分後に毎秒 740,000 回までオペレーションを増やすことをできる

https://gcpug.jp

13 of 61

行Keyの戦略

  • フィールド プロモーション
    • ユーザIDやカテゴリなど、クエリ時に利用する値を行Keyに入れてしまう
      • BATTERY#Corrie#20150301124501001
      • BATTERY#Corrie#20150301124501003
      • BATTERY#Jo#20150301124501002
      • BATTERY#Sam#20150301124501004
      • BATTERY#Sam#20150301124501005
  • ソルティング
    • 行Keyから計算した値を、行Keyに入れる
    • Example (1 ~ 3が出力されるHash関数を利用して、ソルトを生成)
      • BATTERY#1#20150301124501003
      • BATTERY#1#20150301124501004
      • BATTERY#2#20150301124501002
      • BATTERY#3#20150301124501005

13

https://gcpug.jp

14 of 61

Cloud Datastore

  • 分散型KVSのDB
  • Bigtableをアプリケーション向けに利用しやすくしたDB
  • プロパティに対するクエリに対応
  • Transactionに対応
  • 課金体系はRW回数とストレージ容量

14

https://gcpug.jp

15 of 61

GCP Databaseの系譜 (sinmetalの主観)

15

Bigtableをアプリケーションで使いやすいようにQueryやTxを追加し、Bigtableのインフラを共有することでコストを抑えた

Datastoreよりパフォーマンスを改善し、大量のトラフィックを高速に捌けるようになった。

RDBのような柔軟なQueryにも対応

Firebase Realtime DB, Datastore, Spannerのノウハウを融合し、よりGCPらしいRealtime DBへ

Bigtable

Datastore

Spanner

Realtime DB

Firestore

https://gcpug.jp

16 of 61

Cloud Datastoreの後ろ側

16

Bigtableが本領を発揮するにはTB以上のデータが必要だが、通常のアプリケーションはTBもデータがない。

その欠点を克服するためにDatastoreはみんなでBigtableを共有することで、Bigtableをスケールさせている。

Bigtable

Megastore

Datastore

トランザクションを行うためのレイヤー。

プロパティに対するクエリを行うためのレイヤー。

https://gcpug.jp

17 of 61

Cloud Datastoreのクエリの仕組み

  • BigtableはKeyのRangeScanしかできない
  • DatastoreはBigtable上に、Index Tableを作成して、そこをScanするようにしている
    • IndexがなければScanはできないので、必要なIndexがない場合、クエリは実行されず、失敗となる

17

https://gcpug.jp

18 of 61

Cloud Datastoreのクエリの仕組み

  • User Kind
    • Key
    • Name
    • Email
    • CreatedAt
  • User Name Index Asc Kind
    • Name - Key
  • User Name Index Desc Kind
    • Name - Key
  • User Email Index Asc Kind
    • Email - Key
  • User Email Index Desc Kind
    • Email - Key
  • User CreatedAt Index Asc Kind
    • CreatedAt - Key
  • User CreatedAt Index Desc Kind
    • CreatedAt - Key

18

Propertyの値をKeyにしてTableを作成しておく

https://gcpug.jp

19 of 61

Indexの作成タイミング

  • Indexは非同期で作成される
    • put, deleteはIndexの作成を待たずに、レスポンスが返ってくる
      • Indexの種類がたくさんあったり、Indexがhotspotになっていても、パフォーマンスの影響を受けない
    • クエリの実行タイミングによっては古い結果が返ってくる
      • Eventual Consistency

19

https://gcpug.jp

20 of 61

Entity GroupとAncestor Query

  • Entity Group
    • Entity間で親子関係を組むことができる機能
      • order entity
        • key(doaIdjklasa)
      • orderDetail
        • key(order:doaIdjklasa/a98sajw)
        • key(order:doaIdjklasa/daoujda)

20

keyの頭に、親のkeyを入れることで、Bigtable上で連なって保存されるようにしている

https://gcpug.jp

21 of 61

Entity GroupとAncestor Query

  • Ancestor Query
    • 親を指定して子どもを取得するQuery
      • orderDetail.ancestor(key(doaIdjklasa))

21

一度のクエリで親は1人しか指定できないので、JOINとは異なる

子しか取得できないので、親子同時には取得できない

https://gcpug.jp

22 of 61

Firebase Realtime DB

  • Mobile Backend as a ServiceのためのDB
  • SDKがセットになっていて、クライアントから直接読み書きすることができる
  • Realtime DBの名前の通り、Syncの機能がSDKに備わっている
  • Googleが作成したわけではなく、買収した企業が作成したDB

22

https://gcpug.jp

23 of 61

Firebase Realtime DBの後ろ側

  • JSONで管理されている
    • 同じKeyのデータを更新しまくるのは、それなりに得意
    • 大量にデータを入れるとJSONがでかくなりすぎて、つらい

23

https://gcpug.jp

24 of 61

Cloud Spanner

  • 分散型RDB
    • と公式には書いてあるが、実際には分散KVSにRDBの機能を付属しているような感じのDB
  • Bigtable, Datastoreの後継なので、この2つのDBの問題を解決させている
  • Query (Strong Consistency), Transactionに対応

24

https://gcpug.jp

25 of 61

GCP Databaseの系譜 (sinmetalの主観)

25

Bigtableをアプリケーションで使いやすいようにQueryやTxを追加し、Bigtableのインフラを共有することでコストを抑えた

Datastoreよりパフォーマンスを改善し、大量のトラフィックを高速に捌けるようになった。

RDBのような柔軟なQueryにも対応

Firebase Realtime DB, Datastore, Spannerのノウハウを融合し、よりGCPらしいRealtime DBへ

Bigtable

Datastore

Spanner

Realtime DB

Firestore

https://gcpug.jp

26 of 61

Spannerの後ろ側

26

Node

Split

Colossus

Split

Split

Split

Split

Split

Split

Node

Client

Insert, Update, Delete, Queryを処理するレイヤー

それぞれSpan Serverを管理する

1台のサーバというわけではなく、複数Zoneに存在している

Table, Indexが保存されているレイヤー

数は動的に変更される

https://gcpug.jp

27 of 61

Splitの決定方法

  • SpannerはPKの辞書順にデータを保存する
    • PKの先頭付近の文字が重要

27

Split

Split

Split

Split

a ~ e

f ~ j

k ~ o

p ~ z

今、Splitが管理しているKeyの範囲を管理しておけば、指定されたKeyのRowを持っている可能性があるSplitが分かり、そのSplitを担当しているNodeが分かる

Node

Node

Client

https://gcpug.jp

28 of 61

Splitの分割

  • 1つのSplitが大きくなりすぎたり、負荷が高くなりすぎると、Splitを分割する

28

Split

Split

Split

Split

a

f ~ j

k ~ o

p ~ z

Node

Node

Client

Split

b ~ e

a のPKのデータばっかり増えたので、a ~ eを分割

https://gcpug.jp

29 of 61

Splitの再配置

  • Nodeの増減時はSplitの割当が再配置される
  • 1 ~ 2 secぐらい

29

Split

Split

Split

Split

a

f ~ j

k ~ o

p ~ z

Node

Node

Client

Split

b ~ e

Node

https://gcpug.jp

30 of 61

Splitのhotspot問題

  • PKに偏りがあると、Splitにhotspotができてしまう

30

Split

Split

Split

Split

aa ~ af

f ~ j

k ~ o

p ~ z

Node

Node

Client

Split

b ~ e

Split

ag ~ az

hot

spot

hot

spot

hot

spot

hotspotが発生しないようにSpannerはSplitの分割や、Splitの担当のNodeを動的に変更していくが、どうにもならん時もある

ひたすらaのPKのデータが増えていく

https://gcpug.jp

31 of 61

PKの戦略

  • PKはデータの性質に関係なく、分散させておく
  • 公式のベストプラクティスはUUID
  • バッドパターン
    • シーケンシャルナンバー
    • タイムスタンプ
    • 大分類 + 中分類 + 小分類 + ID
      • いずれかの分類にhotspotがあると困る

31

https://gcpug.jp

32 of 61

PKの戦略

32

10,20,30,40,50

INSERT 10, 20, 30, 40, 50

10,20

30,40,50

INSERT 60,70,80,90

30,40,50, 60, 70, 80 ,90

10,20

30,40,50

60,70,80,90

10,20

分割

分割

後ろのSplitにINSERTが偏る

https://gcpug.jp

33 of 61

PKの戦略

33

30,40,50

60,70,80,90

10,20

INSERT 15,23,35,67

30,35,40,50

60,67,70,80,90

10,15,20,23

データが増えていく時に別々のSplitに書き込まれるようにしていく

データがない状態だと、分散していない状態からスタートするので、じわじわデータを入れていく

Cloud Datastoreのベストプラクティス "500/50/5" rule が参考になる

ローンチした瞬間に凄まじいトラフィックが予想される場合は、ダミーデータを入れておくなども有効

NOTE : “500/50/5” rule

新しいKindに対するオペレーションは毎秒 500 回を上限とし、その後でトラフィックを50%ずつ、5 分ごとに増やしていく

理論上は、この増加スケジュールを使用すると 90 分後に毎秒 740,000 回までオペレーションを増やすことをできる

https://gcpug.jp

34 of 61

PKの戦略

  • PKにしたい値から、分散する値を生成する
    • ハッシュ
      • Hash(1) -> Xjead = Xjead@1
    • 逆順
      • ID + 小分類 + 中分類 + 大分類
    • Shard
      • crc32c(2018/06/10 18:36:37 …) % 10 = Modulo @2018/06/10 18:36:37 …

34

https://gcpug.jp

35 of 61

複合Keyの戦略

  • 複数カラムをIDに設定
    • 先頭のカラムの値が分散している必要がある
    • 途中でIDに含めるカラムを増やせない
  • ハッシュ
    • Hash(カラム1 + カラム2 ...)
    • クライアントはHashの値を知らなくても、カラム1, カラム2の値でクエリできる
  • UUID + UNIQUE INDEX
    • UNIQUE INDEXの先頭のカラムの値が分散している必要がある
    • UUIDでクエリしないと遅い

35

https://gcpug.jp

36 of 61

INDEXの仕組み

  • INDEXも結局テーブル
    • INDEXとして指定したカラムをPKとしたテーブルを作成する
      • PKにすれば勝手に辞書順に並ぶ
    • INDEXを設定するカラムもなるべく分散させる
      • 分散しないカラムにはShardなどを併用する

36

DatastoreはIndexを非同期に作成していたが、Spannerは同期的に作るので、Index更新の遅延の影響を受けやすくなってしまった

https://gcpug.jp

37 of 61

TimestampにINDEXを貼るパターン

CreatedAtやUpdatedAtの値はhotspotになりやすい値であり、INDEXも貼りたい値

なので、このソルティングを利用して、INDEXを貼る

now := time.Now()

shardId := crc32.ChecksumIEEE([]byte(now.String())) % 10

id := uuid.New().String()

t := &TweetBenchmark{

ID: id,

Author: getAuthor(),

Content: uuid.New().String(),

CreatedAt: now,

UpdatedAt: now,

CommitedAt: spanner.CommitTimestamp,

ShardCreatedAt: int(shardId),

}

37

https://gcpug.jp

38 of 61

Shard利用時のクエリ

CREATE INDEX TweetBenchmarkIndex ON TweetBenchmark(ShardId, CreatedAt)

SELECT Id

FROM TweetBenchmark@{FORCE_INDEX=TweetBenchmarkIndex}

WHERE ShardCreatedAt BETWEEN 0 AND 9

AND CreatedAt > '2018-05-01T15:14:10.386257Z'

AND CreatedAt < '2018-05-01T15:16:10.386257Z'

ORDER BY CreatedAt DESC;

38

https://gcpug.jp

39 of 61

Shard利用時のクエリ

39

Split

Split

Split

0 ~ 1

6 ~ 7

8 ~ 9

Node

Node

Client

Split

4 ~ 5

Split

2 ~ 3

クエリ時は ShardId BETWEEN 0 AND 9を指定することで、全Splitに対してクエリを実行する

INSERT時はShardIdによってSplitに分散される

https://gcpug.jp

40 of 61

INTERLEAVE

  • 親子関係があるTableを同じ場所に保存しておく機能
  • ほとんどのケースでJOINして一緒に利用するTableを作る場合に使う

40

Order(1)

name:sinmetal

price:1100

OrderDetails(1,1)

itemName:ビスケット

price:100

count:10

OrderDetails(1,2)

itemName:ガム

price:10

count:10

Order(2)

name:gcpug

price:500

OrderDetails(2,1)

itemName:ガム

price:10

count:50

https://gcpug.jp

41 of 61

Cloud Firestore (β)

  • Mobile Backend as a ServiceのためのDB
  • Firebase Realtime DB, Datastoreを融合させたDB
  • Firebase Realtime DBよりスケーラビリティを向上させ、Datastoreの不便なところを修正したDB

41

https://gcpug.jp

42 of 61

GCP Databaseの系譜 (sinmetalの主観)

42

Bigtableをアプリケーションで使いやすいようにQueryやTxを追加し、Bigtableのインフラを共有することでコストを抑えた

Datastoreよりパフォーマンスを改善し、大量のトラフィックを高速に捌けるようになった。

RDBのような柔軟なQueryにも対応

Firebase Realtime DB, Datastore, Spannerのノウハウを融合し、よりGCPらしいRealtime DBへ

Bigtable

Datastore

Spanner

Realtime DB

Firestore

https://gcpug.jp

43 of 61

Cloud Firestoreの後ろ側

43

Spannerが本領を発揮するにはTB以上のデータが必要だが、通常のアプリケーションはTBもデータがない。

その欠点を克服するためにFirestoreはみんなでSpannerを共有することで、Spannerをスケールさせている。

Spanner

Firestore

Mobile Backend as a Serviceの機能を提供するレイヤー。

https://gcpug.jp

44 of 61

Cloud Datastoreからバージョンアップされたこと

  • Strong Consistency
  • Createがある
  • Watchがある
  • Batch操作がatomic
  • 500DocumentまでTxに含めることができる
  • 階層でDocumentを保持できる
  • セキュリティルールが定義できる
    • 型チェックとかもある程度いける
  • マルチテナント機能がない (GAまでには増えそう)

44

https://gcpug.jp

45 of 61

Documentを階層で保持する

  • Order
    • 1
      • name : sinmetal
      • price : 1000
      • details
        • name : ガム
        • price : 10
        • count : 100

45

Documentのフィールドとして、Collectionを持つことができるようになっているので、常にJOINするものは最初から一緒に持たせておくことができる

https://gcpug.jp

46 of 61

SubCollectionの未実装な部分...

  • Order
    • 1
      • name : sinmetal
      • price : 1000
      • details
        • name : ガム
        • price : 10
        • count : 100

46

order.details.name == “ガム” というクエリが実行できない

"collection group query"という機能としてロードマップにあるが、2018/07/14時点ではまだリリースされていない

https://stackoverflow.com/questions/46573014/firestore-query-subcollections

https://gcpug.jp

47 of 61

Cloud SQL Introduction

  • MySQL or PostgreSQLのフルマネージドサービス
  • Masterは1Node
    • zone failover replica
    • read replica
  • Masterが1Nodeなので、WRITEが多いとちょっとつらい

47

https://gcpug.jp

48 of 61

Cloud SQLの後ろ側

48

Your GCP Project

Cloud SQL GCP Project

Compute Engine

MySQL (Master)

Compute Engine

MySQL (Read Replica)

Cloud SQL Proxy

https://gcpug.jp

49 of 61

Cloud Memorystore for Redis (β)

  • Redisのフルマネージドサービス
  • Internal IP Addrからしかアクセスできないので、現状、Compute Engineからしか利用できない

49

https://gcpug.jp

50 of 61

Cloud Memorystore for Redis (β) の裏側

50

https://gcpug.jp

51 of 61

BigQuery

  • ビッグデータ分析のためのサービス
  • PKという概念がない、Transactionがない、UPDATE, DELETEは苦手なので、アプリケーションのDBとしては利用できない
  • ログのように、ひたすら追記しかないような時などは選択肢に入ってくる

51

https://gcpug.jp

52 of 61

Dremel Architecture (BigQueryの裏の仕組み)

HDDから秒間読み込めるデータ容量は百数十MBなので、データを予め100MBほどに分割して、別々のHDDに保存しておく。

クエリ実行時に、必要なテーブルが保存されているHDDに対してDremel Nodeを割り当て、分散処理を行っていく

Dremel Node

Jupiter Network

Storage

Storage

Storage

Storage

Storage

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Dremel Node

Root Node

HDDにHotspotがある場合に備えて、同じファイルを複数のStorageに問い合わせて、最初にレスポンスが返ってきたものを採用している

52

53 of 61

列指向ストレージ

  • 分析時にすべてのカラムを読み込む必要があることは少ないので、カラムごとに分けて保存して、クエリに必要なカラムのみ読み出している
    • カラムの型が決定されるので、圧縮効率も最適化できる

行指向

列指向

53

https://gcpug.jp

54 of 61

Dremel

SELECT

state,

COUNT(1) AS count

FROM

`bigquery-public-data.samples.natality`

WHERE

year = 1987

GROUP BY

state

ORDER BY

count DESC

54

https://gcpug.jp

55 of 61

Dremel v1 Query Processing

55

Leaf

Leaf

Mixer1

Colossus

Jupiter

Leaf

Leaf

Mixer1

Mixer0

Leafはそれぞれ取得したデータに対して以下を実行してMixerに渡す

year = 1987

COUNT

GROUP BY state

MixerはLeafから取得したデータに対して以下を実行して、上のMixerにわたす

COUNT

GROUP BY state

http://gcpug.jp

56 of 61

SpannerとBigQueryの思想の違い

  • BigQuery
    • データ生成時には、どんなクエリを投げるのかは分からないので、フルスキャンを前提とする
    • フルスキャンしやすいように、絡む毎にHDDにみっちり詰めていく
    • テーブルに所属するファイルがどのHDDに保存されているかだけを知っている
  • Spanner
    • クエリはデータ生成時に分かっているので、INDEXを作成する
    • レコードの保存場所はスケールしやすいように、シンプルにソートをメインに考える
    • レコードがどのように分散して保存されているかは常に見ている

56

https://gcpug.jp

57 of 61

Dabatase比較表

57

https://gcpug.jp

58 of 61

Thanks you

Slackで待ってます!

https://slack.gcpug.jp

58

https://gcpug.jp

59 of 61

Resources

59

https://gcpug.jp

60 of 61

Resources for Spanner

60

https://gcpug.jp

61 of 61

Resources for Spanner

61

https://gcpug.jp