gcp datastoreの使い方
概要
- 2024年現在、新規利用する際はfirestoreを利用することが推奨されている
- GCP上で動作するマネージドのnosql
- pythonのdict型のようなデータを保持する
- local emulatorがある
- firestoreという次世代のnosqlもある(若干使い方が異なる, local emulatorのセットアップが異なる)
- bytes型を保存することはできない(Cloud Storageと連携を期待している)
ローカルエミュレータの使用法
エミュレータのインストール
$ gcloud components install cloud-datastore-emulator
- メモリーエラーが発生する時
- 環境変数のJavaのメモリを増やす
- e.g.
export JAVA_TOOL_OPTIONS="-Xmx16g"
- e.g.
- 環境変数のJavaのメモリを増やす
ローカルエミュレータの起動
$ gcloud beta emulators datastore start
エミュレータを動作させるための環境変数のロード
$ $(gcloud beta emulators datastore env-init)
具体例
公式の例(エンティティを新規に追加する)
from google.cloud import datastore
import datetime
client = datastore.Client()
kind = "Task"
name = "sampletask1"
task_key = client.key(kind, name) # キーを取得
task = datastore.Entity(key=task_key)
task["description"] = "Buy milk" # dict型でデータを挿入できる
client.put(task) # 保存
print(f"Saved {task.key.name}: {task['description']}")
一括でデータを入れ、エンティティを追加する
""" データを入れる例 """
key = client.key('users', '213')
task = datastore.Entity(key)
task.update({
'name': 'Bob',
'transaction_id': 1878,
'timestamp': [datetime.datetime.now()],
'revenue': [135.55, 222],
'paid': True
})
client.put(task)
すべてのデータを取り出す例
client = datastore.Client()
docs = client.query(kind='users').fetch()
for doc in docs:
print(doc.key.name)
print(doc)
存在しないキーを参照したとき
""" 存在しないキーの例 """
client = datastore.Client()
key = client.key('users', '0000')
task = datastore.Entity(key)
assert(task == {})
特定の値を検索
""" 特定の条件に一致する結果を取り出す例 """
client = datastore.Client()
docs = client.query(kind='users').add_filter('paid', '=', True).fetch()
for doc in docs:
print(doc.key.name)
print(doc)
特定のキーのエンティティを取り出す
client = datastore.Client()
with client.transaction():
key = client.key("users", user)
if task := client.get(key):
logger.info(task)
値があるかどうかを検索し、なければエンティティを追加、あれば値を更新
from google.cloud import datastore
import datetime
import numpy as np
from loguru import logger
from tqdm.auto import tqdm
client = datastore.Client()
def update_data(user: str):
with client.transaction():
key = client.key("users", user)
if task := client.get(key):
# logger.info(task)
task["datetimes"].append(datetime.datetime.now())
while len(task["datetimes"]) > 3:
task["datetimes"].pop(0)
client.put(task)
else:
task = datastore.Entity(key=key)
task.update({
'name': key,
'datetimes': [datetime.datetime.now()]
})
client.put(task)
for _ in tqdm(range(10**5)):
chrs = np.random.choice(list("1234567890"), size=3, replace=True)
update_data(user="".join(chrs))
特定のプロパティをインデックスしない(デフォルトではすべてのプロパティがインデックスされる)
- 検索で使わないプロパティは除外しておくとディスク容量を削減できる
task_key = client.key(kind, key) # キーを取得
task = datastore.Entity(key=task_key, exclude_from_indexes=("data",)) # データのプロパティは除外する
task["data"] = data # 値を入れる
client.put(task) # 保存
トラブルシューティング
io.grpc.StatusRuntimeException: RESOURCE_EXHAUSTED: gRPC message exceeds maximum size …と出てデータをやり取りできない
- 原因
- 読み書きのデータが大きすぎる
- 対応
- 一度にやり取りするデータを減らす