注文を全部読むのか
ネットショップに、100万件の注文があります。ある客が、自分の注文を見たいと言いました。
店は、100万件を全部読んで探すのでしょうか。よくできた DynamoDB の表は、そうしません。クエリは、その客の注文へ直接行き、ほかは読みません。このレッスンでは、クエリがスキャンより安い理由と、答えの数の読み方を学びます。
このレッスンのゴール
メモ
クエリがスキャンより安い理由を、説明することができる。
キーワード
| ことば | いみ |
|---|---|
| パーティションキー | 項目をどこに保存するかを決める属性 |
| ソートキー | 同じパーティションキーの中で、項目を並べる属性 |
| キー条件 | クエリがキーに対して行う判定 |
| クエリ | 1つのパーティションキーへ行く読み取り |
| スキャン | 表のすべての項目を見る読み取り |
| フィルター | 項目を読んだあとに行う判定 |
学生が入力するセルは、このプレビューでは空白です。
2つのキーが住所になる
この注文の表には、キーになる属性が2つあります。パーティションキーは customer です。サービスが項目をどこに保存するかを決めるので、1人の客の注文は、まとまって並びます。
ソートキーは order_id です。1人の客の注文を、順番に並べます。2つのキーが合わさって、項目がちょうど1つに決まります。同じ客の注文は何件もありますが、2つのキーが両方同じ注文はありません。
例: 注文の並び方
| パーティションキー: customer | ソートキー: order_id | ほかの属性 |
|---|---|---|
| sato | 1 | ramen、paid、total 4 |
| sato | 2 | tea、new、total 2 |
| sato | 3 | cake、paid、total 15 |
| suzuki | 7 | ramen、paid、total 8 |
| tanaka | 4 | tea、new、total 6 |
| tanaka | 9 | ramen、paid、total 3 |
学生が入力するセルは、このプレビューでは空白です。
クエリは1か所へ、スキャンは全部へ
クエリは、パーティションキーを必ず指定します。指定しないと、サービスは ValidationException と HTTP 状態 400 で断ります。サービスはその客へ直接行き、その客の注文だけを読みます。
スキャンは、何も指定しません。表のすべての項目を、1件ずつ読みます。どちらの答えにも、Count と ScannedCount があります。Count は返した項目の数、ScannedCount は見た項目の数です。
実行: クエリとスキャン
orders = [
{"customer": {"S": "sato"}, "order_id": {"N": "1"}, "status": {"S": "paid"}},
{"customer": {"S": "sato"}, "order_id": {"N": "2"}, "status": {"S": "new"}},
{"customer": {"S": "sato"}, "order_id": {"N": "3"}, "status": {"S": "paid"}},
{"customer": {"S": "suzuki"}, "order_id": {"N": "7"}, "status": {"S": "paid"}},
{"customer": {"S": "tanaka"}, "order_id": {"N": "4"}, "status": {"S": "new"}},
{"customer": {"S": "tanaka"}, "order_id": {"N": "9"}, "status": {"S": "paid"}},
]
def scan(items):
return {"Count": len(items), "ScannedCount": len(items)}
def query(items, customer):
found = []
for item in items:
if item["customer"]["S"] == customer:
found.append(item)
return {"Count": len(found), "ScannedCount": len(found)}
print("スキャン:", scan(orders))
print("sato のクエリ:", query(orders, "sato"))
# "sato" を "suzuki" に変えましょう。クエリが見る項目は、いくつになりますか。
フィルターは答えを減らすが、仕事は減らさない
キー条件では、ソートキーも判定できます。たとえば、order_id が 2 以上、という形です。するとサービスは、パーティションのその部分だけを読みます。
フィルターは、status のような、ほかの属性を判定します。項目を読んだあとに働くので、Count は変わりますが、ScannedCount は変わりません。
例: sato の paid の注文、2番から
| 手順 | 残る項目 | 何が起きたか |
|---|---|---|
| パーティションキーが sato | 3 | 注文 1、2、3 |
| ソートキーが 2 以上 | 2 | 注文 2 と 3。ScannedCount は 2 |
| フィルター: status が paid | 1 | 注文 3 だけ。Count は 1 |
学生が入力するセルは、このプレビューでは空白です。
よくある間違い
注意
フィルターを使うと、読み取りが安くなる、と考える人が多いです。安くなりません。サービスは先に項目を読み、あとから一部を捨てます。できるだけキー条件に入れ、残りだけにフィルターを使います。
実行: 2つの数を読む
answer = {
"Count": 1,
"ScannedCount": 2,
"Items": [
{"customer": {"S": "sato"}, "order_id": {"N": "3"}, "status": {"S": "paid"}},
],
}
dropped = answer["ScannedCount"] - answer["Count"]
print("見た数:", answer["ScannedCount"])
print("返した数:", answer["Count"])
print("フィルターが捨てた数:", dropped)
# ScannedCount を 3 に変えましょう。フィルターが捨てた数は、いくつになりますか。