Datastoreにpythonで触ってみたメモ

ここ半年くらい前から少しずつ、GCPのDatastoreを触っています。

今更ながらDatastoreを触ってみた雑感をメモしてみます。
DynamoDBとの比較とかしたいですが、ほとんどNoSQLの特徴的な感じです。
あまり、RDB->KVSの時の設計指針や思想の転換について教えてくれる記事とかなかったのでまとめてみました。

[「RDB技術者のためのNoSQLガイド」](http://www.amazon.co.jp/RDB%E6%8A%80%E8%A1%93%E8%80%85%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AENoSQL%E3%82%AC%E3%82%A4%E3%83%89-%E6%B8%A1%E9%83%A8-%E5%BE%B9%E5%A4%AA%E9%83%8E/dp/479804573X)
こういう本にある程度書かれているのかな、と思いますが、Datastoreのことは載ってなかったような気がします。。

ちなみにGAE/pyから触っています。

# RDBの概念との対応
まずは基本の用語の整理から。

[データベース新基礎知識 Googleの巨大分散データストアBigtableとDatastoreを理解する （4/12）](http://itpro.nikkeibp.co.jp/article/COLUMN/20110107/355908/?P=4)
この記事に書いてありましたが、

| datastore|   RDB  |
|:--------:|:------:|
| kind     | table  |
| entity   | record |
| property | field  |

らしいです。

# Datastoreの特徴

自分がテーブル設計の時に意識すべきこととして考えたことをまとめてみました。
基本的にDatastoreというかスキーマレスなNoSQLにはよくある概念かと思います。

### テーブルがない
Datastoreはテーブルという概念はなく、一つの場所に複数のkindのentityを入れて管理しています。
そのため、kindがtableのような役割をしているように見えます。

ちなみにnamespaceというGCP？GAE？が持っている概念もあり、それを使うと同一プロジェクトに独立したDatastoreを作ることができます。

### 複数kindのトランザクション
複数kindはentity groupに入れることでトランザクション内で同時に情報を更新できます。
ただし、一つのentity group内では1/sec程度しかputができない、という制約があるみたいです。

### keyを使ったgetは早い
keyしてのentity getはとても早いです。
propertyのgetはentityをgetした後にしかできません。
なので、queryはkeyのリストしか取得できないので、普通にクエリ発行していると、内部的に中身を遅延取得していたりするみたいです。

### 整合性
整合性についてのトレードオフがあります。

entity groupに入れない普通のputなら結果的整合性が担保されています。
これは結果がすぐに反映されず、クエリによっては古い内容をしばらく返します。（ノードの都合？）
entity groupに入れれば更新頻度が制限される代わりに強い整合性が担保され、すぐに新しい情報を取得することができるようになります。

# 設計上の注意

## viewから設計を始める
データ管理の観点からするととても違和感がありますが、datastoreの設計ではView、つまりデータの表示や加工のされ方を念頭にオブジェクトを設計していく方がいいようです。

つまり設計段階できちんと、データの取得・更新ユースケースを想定しておく必要があります。
例えば、ユーザリストを取得するのか、dataを取得するのか。など。

なぜかというと、次に述べる非正規化と関連するのですが、クエリをなんども発行するとどんどんAPIが時間がかかります。
UX的にも悪いですし、GAEを使っているなら1分間の制約もあります。
なので、そもそも表示するようなものはデータとして一緒に持ちましょう、という考え方をした方がいいようです。
RDBでの設計指針は一旦捨てましょう。

## 非正規化のススメ
DatastoreはRDBと違い、集計系の処理がほぼ不可能だったりします。
そのため、集計というか、あらかじめ参照することがわかっている情報はなるべく全てのテーブルに持たせるとか、そういうテクニックを推奨している記事が多かったです。

## なるべくkeyで取得
これが個人的には一番ポイントかなと思います。
検索やクエリがあると、何かとクエリで取得してしまいますが、結局KVS(厳密には違うかもしれませんが)は、keyをトリガーにした取得で真価を発揮します。
更新時の整合性もkey取得なら担保されていたりします。
そして、後で気付いたのですが、トランザクション内ではkeyで取得しかできませんw

## リスト取得はkeyのみで
これはあまり実践できていないですが汗
全propertyを取得しようとするのではなくkeyリストを取得してから特定数のentityを取得していく方が早いです。
もし名前だけ必要だったりするケースではkeyだけ取得オプションをつけて取得して表示しましょう。

# 参考にした記事

データの設計のさいに参考になりそうな記事を探してみました。
どれも古い記事なのですが、ある程度は参考になりそうです。

ただ、ワークアラウンド的な情報もあるので、その辺りはアップデートによって不要になったりしているかもしれません。
特にkeyにプロパティ情報の一部を入れておいてkeyリストを取得すれば、entityの中身を見なくても良い、という方法がちょっと特殊な感じがしています。

### [ITPro クラウド設計のデザインパターン［Google App Engine編］スキーマ設計の工夫で検索速度を上げる](http://itpro.nikkeibp.co.jp/article/COLUMN/20110315/358361/?ST=cloud)
SQLを使うRDBとどのように設計方法が違うか、非正規化という観点から説明されていてわかりやすかったです。

書いてあること： 
 
* 表示内容に含めるための非正規化
* 検索条件に含めるための非正規化
* 集計機能を使うための非正規化
* 特定のクエリを高速化するための非正規化（検索用kind作成）
* 整合性を保つための非同期更新
* keyに情報を含んでしまえばqueryが早く済むハック

### [Google App Engine上のベスト・プラクティス、その１: Datastore](http://satoshi.blogs.com/life/2010/02/app_engine.html)
中島聡さんのブログでもDatastoreについて書かれていました。
こちらにも非正規化がオススメされていますが、他にもentity groupの使い方や設計方針などについてもわかりやすかったです。
ただ、クエリ速度の問題やエラー率の多さは、このブログの頃よりもだいぶ改善しているかな、という印象を受けました。 

書いてあること：  

* get/putは最小限に。多くなるなら非同期で。
* entity groupはトランザクション目的で。なるべく小さく。
* 非正規化をする
* join(nested query)しなくて済むようなデータ設計
* いかにqueryをkey取得だけに抑えるか。keyに情報を入れるノウハウ
* keyを使ったqueryを心がける
* Datastoreの失敗率は高いからリトライ前提にしよう
* SQLとかGQLとか使うのは意味がない

## [[gae]エンティティを２つに分けます - How BuddyPoke Scales on Facebook Using Google App Engine ](http://d.hatena.ne.jp/urekat/20100124/1264298323)
get用とput用のentityを分割する方法もあるようです。(kindも違う気がする)
こちらはそれほど実用的ではないかもしれません。

## [Google Cloud Datastoreでのデータ整理の考え方 ](http://googlecloudplatform-japan.blogspot.jp/2015/08/google-cloud-datastore.html)
去年のgoogle 公式ブログですが、入門にはとても参考になります。

* ファイルパスメタファーが強い整合性のためのentity groupの使い方の参考になる。
* rootに配置されたそれぞれの拡張子のentityを更新したり追加したりする。  
lsはまあ早いし、特定ファイルの取得はもっと早い。
* writeは遅い。
* 関連づけにdirectoryのようなentity groupで親子関係を形成させる。
* 一つのdirectoryに対する書き込みは制限されるが取得するときに結果がすぐに反映される。