たれぱんのびぼーろく

わたしの備忘録、生物学とプログラミングが多いかも

AWS DynamoDB 頭からしっぽまで

Amazon DynamoDB

Amazon DynamoDBAmazon Web Serviceが提供するデータベースサービスの1つ。
データベースの分類ではNoSQL/Key-Value Storeに分類され、高い読み書き性能を誇る。一方で幅広い検索には最適化されていない。
サービスとしてはほぼ設定をおこなわずに高いパフォーマンスの従量課金型プランを利用でき、特にWebサービスのバックエンドDBとして第一候補になりうる。

利用方針概論

"高度な最適化"が不要なアプリケーションは

  • サービス: AppSyncフロント+DynamoDB on-demand
  • 実装: AWS Amplify スキーマ駆動(Amplify Frameworkでclient app自動生成、Amplify CLIでCFn Templates自動生成)

が最善手。
高いパフォーマンスをほぼ0設定、かつ従量課金で得られる。
開発/実装もAmplifyのschema駆動生成を利用すればすぐに終わる.

わかる人向け: hot partition・lambda cold startは上記組み合わせでは発生しないので安心してのっかっていい.

データベース構造

Table-item-attributeの3階層をもつ.
Tableは1つの独立したデータベース.
itemはアクセス単位.
attributeは値をもつitemのattribute

Tableは任意個のitemを、itemは任意個のattributeを持つ.
各itemは任意の構造を持つ(attributesを持つ)ことができ、きちんと構造化された格子状のデータ構造を持たなくてもいい.
各itemは自分のもつ特定attributesから成る識別子があり、識別子を構成するattributesだけは全itemに必ず求められる.
f:id:tarepan5884:20180212220823p:plain

識別子

DynamoDBはKVS (Key-Value Store) なのでKeyについて理解することが根幹.
DynamoDBのKeyはitemのattributeからなる.
全てのitemに必ず設定されるkeyがPrimaryKey(主キー).
PrimaryKeyは1つあるいは2つのAttributeからなる。2つの場合、各々はpartitionKey・sortKeyと呼ばれる.
全てのデータアクセスはこのPrimaryKey等のKeyによっておこなわれる.
なのでPrimaryKeyは必ず一意にならなければならない.

例えばTableXのPrimaryKeyが "id" attributeに設定されているとすると、次のitem

{
  id: 12345,
  name: "Panda",
  age: 5
}

のKey値は"12345"になる.
よってTableXから "key = 12345" でgetを要求すると上記のアイテムが取り出される.
keyは一意に決まらなければならないので、"id" attributeが12345になっているitemはこれしか存在できない.
もしTableXのPrimaryKeyが(partitionKey: id, sortKey: name)だとしたら、このitemのPrimaryKeyは"(12345, Panda)"になる.
この場合、PrimaryKeyが一意になっていればいいのでkeyが(12345, Usagi) や (54321, Panda) のitemは存在出来る.

データアクセス

Read

Readアクセス方法は3種類 Key-Value storeなので、Keyを指定して値を取り出してくるのが基本.

  • Query: key指定 1
    • key完全一致
    • partitionKey一致 + sortKey絞り込み
  • Scan: 全件取得 2

Query("12345+Panda")でitemを1つ取ってきたり、sortKeyが数字だったら Query("12345", 100<sortKey<200) でsortKeyに基づいた部分的queryをしたりできる。
Scanはシンプルで全件取得。取得後(取得コスト発生後)にfilterをかけて戻ってくるデータ量を減らすことはできる.

ネットワーク経由アクセス

Read(QueryおよびScan)やWriteをネットワーク経由で行うことができる.

  1. AWS-sdkから使う
  2. API Gatewayを経由する
  3. AppSyncを経由する

1. AWS-sdkから使う

Javascript aws-sdkを用いて認証・アクセスをおこなう.
docs.aws.amazon.com

パーティション

内部の仕組み
パーティションとデータ分散 - Amazon DynamoDB

クエリの柔軟性: セカンダリインデックス

DynamoDB はプライマリキーを使用してテーブルの各項目を一意に識別し、セカンダリインデックスを使用してクエリの柔軟性を高めます。
Amazon DynamoDB のコアコンポーネント - Amazon DynamoDB

インデックスは高速化をもたらしてくれる
ただし更新コストが高くつきうるのでお気をつけて.

インデックスは代替のクエリパターンへのアクセスを付与し、クエリを高速化できます。
...
インデックスの作成は慎重に判断する必要があります。 テーブルに書き込みが発生するたびに、テーブルのインデックスはすべて、更新する必要があります。 大きなテーブルでの書き込み量が多い環境では、大量のシステムリソースを消費する可能性があります。
インデックス管理 - Amazon DynamoDB

スループット

キャパシティーユニットをいくつ割り当てるかで最大スループットが決まり、消費量はitemサイズで決まる.

Read

  • GetItem: itemサイズ (4KB単位で切り上げ)
  • Query: 取得itemサイズ合計 (4KB単位で切り上げ)
  • Scan: テーブル内itemサイズ合計 (4KB単位で切り上げ)

Scanはヤバい、でかいテーブルだとキャパシティを大量に消費してしまう.
1読み取りキャパシティーユニットは4KB/sなので、1キャパシティユニット == 小さい1操作 という感覚. scanはヤバい

GetItem – テーブルから単一の項目を読み取ります。GetItem が消費するキャパシティーユニットの数を決定するには、項目のサイズを次の 4 KB 境界まで切り上げます。...
たとえば、3.5 KB の項目を読み取ると、DynamoDB は項目サイズを 4 KB まで切り上げます。

Query - 同じパーティションキー値を持つ複数の項目を読み取ります。返されるすべての項目は単一の読み取りオペレーションとして扱われ、DynamoDB はすべての項目の合計サイズを計算し、次の 4 KB 境界に切り上げます。たとえば、クエリの結果、合計サイズが 40.8 KB になる 10 項目が返されるとします。DynamoDB はオペレーションの項目サイズを 44 KB まで切り上げます。クエリの結果、64 バイトの項目が 1,500 項目返されると、累積サイズは 96 KB になります。

Scan - テーブルのすべての項目を読み取ります。DynamoDB は、スキャンにより返される項目のサイズではなく、評価される項目のサイズを考慮します。
DynamoDB 項目サイズと形式 - Amazon DynamoDB

Queryの消費キャパシティユニット

query対象itemの合計量から消費CUを切り上げ計算。
各itemが切り上げられるわけではない(100Byte * 10 item ≠ 10 CU)。

このテーブルのCUが消費されるか -> AWS - docs - DynamoDB - DynamoDB でのクエリの操作 - クエリで消費されるキャパシティーユニット

Query — 同じパーティションキー値を持つ複数の項目を読み取ります。返されるすべての項目は単一の読み込みオペレーションとして扱われ、DynamoDB はすべての項目の合計サイズを計算し、次の 4 KB 境界に切り上げます。たとえば、クエリの結果、合計サイズが 40.8 KB になる 10 項目が返されるとします。DynamoDB はオペレーションの項目サイズを 44 KB まで切り上げます。クエリの結果、64 バイトの項目が 1,500 項目返されると、累積サイズは 96 KB になります。
ref

料金

従来は事前設定したキャパシティ (+auto-scaling) に課金されていた。が、従量課金型DynamoDBも出た。
事前設定と従量課金がバランスするのは、事前設定の14.4%とのこと ref
従量課金型はバーストトラフィックに強いので、平常時がやたら低かったり、バーストが時よりおきるようなら従量課金が向いているはず.
どうしても容量オーバーを許容できない場合も従量課金が向いている.

DynamoDBは割り当てスループット (処理速度) で料金がおおよそ決定する.
その計算方法がやや独特
docs.aws.amazon.com
docs.aws.amazon.com

従量課金(オンデマンド)

aws.amazon.com

データストレージ 0.25USD/GB
データ転送 (only out) 0.09USD/GB
Readリクエスト 1.25USD/100 万req
Writeリクエスト 0.25USD/100 万req

良い資料

www.slideshare.net
docs.aws.amazon.com

PutItem

KeyとattributeValues
condition checkしてtrueなら書き込み(上書き含む)

キャパシティ

Read/Write Capacity Mode

オンデマンド

  • request types
    • action
      • read
      • write
    • character
      • strongly consistent (READ only?)
      • eventually consistent
      • transactional

e.g. eventually consistent read request

capacity units

  • read request unit: two "eventually consistent" | single "strongly consistent" | 0.5 "transactional" read request for a <4KB item
  • write request unit: single "normal" | 0.5 "transactional" write request for a <1KB item

Peak Traffic and Scaling
自動設定値 (peak settings) の倍まで即座にスケール.
peak settingsは前回の最大値.
初期値は以下.

  • initial: 以下のどちらか(組み合わせ可)
    • write request units per table: 2,000/sec
    • read request units per table: 6,000/sec

Initial Throughput for On-Demand Capacity Mode

1,000 WRU + 3,000 RRU とか.

peak settings更新は30分ほどを見込む必要あり.

However, throttling can occur if you exceed double your previous peak within 30 minutes.
official

上限はread 40,000 write 40,000 units.
リクエストすれば上限解放あり.
ref

adaptive capabilityがあるのでパーティション間のcapacity分配は基本的に考えなくていい.

容量まわり

縛りはただ1つ.
LSI(local secondary index)を使用する場合は、単一のpartitionKeyに対して10GB以下 (PrimaryKey中のSortKeyはLSIじゃないので、SortKey設定しただけではならない).
パーティションが1つのサーバーに入っているので、LSIを色々振ろうとするとindexing用データ(コピーしているらしい ref)が増えるからmaxの5LSIでもサーバー1台にきちんと収められる10GBにしよう、ってことだと思う.
PrimaryKeyの場合はOKなのいまいちわかっていない(1primaryKeyで100TBにいったら1台のサーバーに収まらないのでは…?どうも大量のストレージを割り当てているっぽいのだが...)

DynamoDB テーブルには、パーティションキーの値ごとに個別のソートキー値の数に上限はありません。Pets テーブルに数十億の Dog 項目を保存する必要がある場合、DynamoDB は自動的に十分なストレージを割り当てて、この要件に対応します。
ref

Dog項目が "AnimalType": "Dog"をpartitionKeyにしている文脈での引用文なので、ストレージすごい割り当てているっぽい…すごい…
なにか分散の仕組みがあるっぽい(回答をまだ読んでいない)
stackoverflow.com

partitionとpartitionKeyとの関係を考えたい.
partitionは<10GBで、partitionKey == Xが10GBを超える場合がある(禁止されていない)ので、1 partitionKey : N partitionがありうる.
わかりやすくpartitionKey == X < 10 GBとすると、partition数パターンを出力するhash関数にprimaryKeyを通す.
あるpartitionKeyの総容量はわかっているので、hash後にそのhashに対応する容量がわかる。
それが10GBを超える場合もある (9.9GBのpartitionKey + 0.2GBのpartitionKeyで超えちゃう)
hashし直すのか?あるいはhash of hash with sortKey?

利用論

DynamoDB(データモデル)設計ベストプラクティス: request中心に設計せよ

requestの型、すなわちAPI Schema
schema-firstなら、schemaから自動プロビジョニングするのは自然
DynamoDB Tableの設定可能事項は、最適化しないなら選択肢がほぼ無い

かつての課題

  • ホットパーティション問題(過剰キャパシティ)
  • cold start問題(Lambdaフロント)

現在ではあまり問題にならない。
DynamoDB on-demandはqeury量に比例して課金されるので、キャパシティ設計はいらず、ホットパーティションがあってもquery量はかわらないので問題ない。
AppSyncはGraphQLを利用しており、DynamoDB向けのQuery解決 (リゾルバ) にはVTL(テンプレートエンジン/言語)を用いているのでcold startがない (Lambdaはnode環境等を含めて立ち上げる必要があるのでcold startがある)。

高度な最適化が必要な場合

  • 料金最適化 (平均キャパシティ利用率15%がon-demandと通常のキャパシティ割り当てが平衡)
  • 複雑な検索(パーティション/セカンダリindex設計がキャパシティ使用量へ大きく影響)

小データ多頻度Write

Capacity Unitの切り上げ => 50Byteのデータを100record/secとかで書き込もうとすると凄い費用がかかる
batchでrecordを1つのwriteとみなすような仕組みもないため、50B・100 record/secは50WCU/secになる。

ログをDynamoDBへ直接しまおうとするとこうなる.

firehoseは純粋にデータ量比例.

解析がマネージドサービスの範囲で済むならfirehose-S3-kibanaとかがいいと思う
より込み入ったことが必要な場合、データを取り出して自分でいじいじする形.
=> 外部アクセスが容易なDBが欲しくなる
S3+BIツール、とかはデータベースという感じではない。ログセット+可視化ツール的な

ログをpackしてDynamoDBに入れるよろし.


  1. The Query operation in Amazon DynamoDB finds items based on primary key values Working with Queries in DynamoDB

  2. A Scan operation in Amazon DynamoDB reads every item in a table or a secondary index. Working with Scans in DynamoDB