ダイナモスキャン:最も効率的な操作😉


あなたがスキャンを避けるべきである多くの場所で読むことができるので、タイトルは目的で挑発的です、そしてScan operations are less efficient than other operations in DynamoDB . 私は、危険があると思います、何が後ろにあるかについて理解しないで、それらのメッセージを読んで、人々が実際に走査を避けて、さらにより悪い何かによって彼らを交換すると思います.あなたが操作の効率を比較したいならば、あなたは同じことをするとき、それを比較しなければなりません、あるいは、それはアップル対オレンジの比較です.ここで私は2つの極端なユースケースと比較します:すべてのアイテムを取得する必要があり、1つのアイテムのみを取得する必要があります.そしてさらに、「避けるスキャン」アイデアの後ろにあることをさらに説明します.
私は5000項目のテーブルを作成しました.

aws dynamodb create-table --table-name Demo \
 --attribute-definitions AttributeName=K,AttributeType=N \
 --key-schema            AttributeName=K,KeyType=HASH \
 --billing-mode PROVISIONED --provisioned-throughput ReadCapacityUnits=25,WriteCapacityUnits=25

for i in {1..5000} ; do
aws dynamodb put-item     --table-name Demo --item '{"K":{"N":"'${i}'"},"V":{"S":"'"$RANDOM"'"}}'
done
私は小さなテーブルの上でデモをするたびに私は“これは何も証明していない、テーブルはあまりに小さい”私はあなたがそれがどのようにスケールを理解するためにpetabytesを必要としないことを正確にする必要がありますとコメントを人々がいるので.特にリニアにスケールするように設計されているDynamoDBで:あなたはRDBMS(キャッシュで最適化された小さなスキャン、ストレージインデックス/ゾーンマップで最適化された大規模なスキャン)で持つことができるように、しきい値に到達した後に起こる魔法はありません.疑問があるならば、同じことを実行し5000人を5000万に変更することもできますし、同じことを観察するでしょう.しかし、あなたは私のものではなく、あなた自身のクラウド・ビルでそれをします.)
項目を数えましょう:

[opc@a DynamoDBLocal]$ aws dynamodb scan --table-name Demo --select=COUNT --return-consumed-capacity TOTAL --output text

5000    None    5000
CONSUMEDCAPACITY        6.0     Demo
これはスキャン操作です.消費容量は6 RCUである.これは良いか悪いか?効率的?
まず、それらの6つのRCUを理解しましょう.私は5000の項目を持っています、彼らのサイズは10バイト(2文字までの1つの性格の2つの属性)のビット未満です.これは約48キロバイトで、最終的な一貫性(私たちはすべてのミラーを読んでいない)で読みます.数学は簡単です:48/4/2 = 6.あなたが小さなテストケースを信じていない人のために提案したように5000万個のアイテムでそれをテストする場合は、600万RCUが表示されます.それはちょうど初歩的な算術演算子、クロス乗算し、それを得る、魔法がありません.それで、あなたがデフォルトで40000 RCU/Secondであると思う最大のオンデマンドRCUを供給するならば、あなたは2分半でそれらの5000万のアイテムを数えることができます.それは非効率的か?並列スキャンを試してください.
スキャン
あなたは、私がどこに来ているかについて見ます.「回避」または禁止する操作はありません.それはちょうどあなたがしたいことに依存します.すべての項目をカウントするスキャンで行われ、高速DynamoDBで行うことはできません.グローバルカウンタを維持する場合を除き、各putitemのコストを2倍にします.あなたはそれを高速にしない、あなただけのアプリケーションの別の部分にコストを転送します.
あなたは、カウントより複雑な何かをしたいかもしれません.属性vの値を合計するスキャンです.

[opc@a DynamoDBLocal]$ aws dynamodb scan --table-name Demo --select=SPECIFIC_ATTRIBUTES --projection-expression=V --return-consumed-capacity TOTAL --output text \
| awk '/^CONSUMEDCAPACITY/{rcu=rcu+$2}/^V/{sum=sum+$2;cnt=cnt+1}END{printf "%10.2f rcu   %10d items %10d sum(V)\n",rcu,cnt,sum}'

      6.00 rcu         5000 items   81599797 sum(V)
このコードはペグネーションを処理します(ここでは私のテーブルが1 MB以下である必要はありませんが、5000万個のアイテムを試している人にとっては、これをコピー/ペーストできます).私は、AでスキャンPaginationを記述しましたprevious post それで、あなたはなぜ私がなぜ「テキスト」出力をここで使うかについて理解します.いいえ驚き、スキャンはスキャンされているとDynamodbのキャッシュがないときに頻繁に同じデータを読むときに高速にする:6 rcu再び.
地質調査所
次に、あなたは彼らがスキャンを避ける必要がありますあなたの開発者に伝える場合はどうなりますか?テーブルデザインは既にあり、カウントと合計を得る必要があります.これは重要なユースケースではなく、毎日のダッシュボードに表示するだけかもしれません.したがって、ラムダやAWS接着弾性ビューでカウンタを維持するオーバーヘッドを追加する必要はありません.スキャンはここで完全に有効です.しかし、彼らはこの「非効率的なスキャン」を回避しようとします、そして、次に、この考えで来ます:彼らは、最後のアイテム番号が挿入されたことを知っています(私のデモで5000)、そして、「効率的な」getitem呼び出しを使用してください

[opc@a DynamoDBLocal]$ for i in {1..5000} ; do  aws dynamodb get-item --table-name Demo --key '{"K":{"N":"'$i'"}}' --return-consumed-capacity TOTAL ; done \
| awk '/^CONSUMEDCAPACITY/{rcu=rcu+$2}/^V/{sum=sum+$2;cnt=cnt+1}END{printf "%10.2f rcu   %10d items %10d sum(V)\n",rcu,cnt,sum}'

   2500.00 rcu         5000 items   81599797 sum(V)

あなたがそれがどのように働くかについてわかっているならば、驚きはありません:各々のgetitemコスト0.5 Ru、そして、合計は2500 RCUです.ほとんどの場合、ストレージからデータの同じブロックを読みますが、これはまだRCUとしてカウントされます.これは、スキャンより416倍高価です.それで、「スキャンは最も効率的な操作」主張を洗練しましょう.
  • スキャンは1つの項目を得るために最悪の効率的な操作です
  • スキャンは、最も効率的な操作多くのアイテムを取得することです
  • サイズ
    ここで「多く」とは何ですか?私がここでしたように、すべてのアイテムを得ることはスキャンが最も効率的であるところです.しかし、私の例では、getitemはアイテムごとに0.5 rcu、スキャンコストは6 rcuとなるので、スキャンは12個以上のアイテムを得るときに最も効率的な操作であると言える.しかし、これは2つのことによる.最初に、どの述語がそれらの12の項目をフィルターにかけるかによって、質問はスキャンより速いかもしれません.これはデータモデルによって異なりますが、ここでは私のテーブルではありません.第2に、この12の要素は項目のサイズに依存する.なぜなら
  • スキャン操作は、テーブルのサイズ(すべての属性を持つすべての項目)に依存し、項目の数が読み込まれない
  • getitem操作は項目数の数に依存します(そして、4 KBより大きいときのサイズ).
  • 私の例では、私は小さいアイテム(10バイト)を持っています、そして、スキャン猫は0.5 RCUにつき400以上のアイテムを得ます.ここで、getitemはRCUごとに最大で1アイテムを得ることができます.これにより、走査はgetitemより迅速に効率的である.これはテーブルのサイズによって異なりますが、各項目のサイズによって異なります.これは重要ですbest practice documentation また、「大きなテーブルやインデックスのスキャン操作を使用して、多くの結果を削除するフィルタを使用することは避けてください.「避けてください」というのは絶対的なものですが、これは本当ですが、どんな操作にも当てはまるでしょう.データを読むのを避けてください.getitemのように別のアクセスタイプを使用して「避ける」ようにすれば、これは間違っています.テーブルサイズは効率をカウントしません.この主張は、「多くの結果を取り除くフィルタ」がパーティションキーの等値述語であるときに限ります.しかし、この時点で開発者がこれを読んで、テーブルのデザインが行われ、それは遅すぎる.NOSQLでは、RDBMSの論理的データの独立性を持たないので、コードの巨大なリファクタリングを行わずにパーティションキーを変更するための敏捷性はありません.このユースケースのためにできるのは、スキャンして、たぶんあなたのアプリケーションコード、またはDAXサービスをキャッシュします.
    すべてこれはSQLの人々のための新しいされていません.“フルテーブルスキャン悪の”この神話は非常に古いです.そして、人々は、最後の数十年で起こったすべての最適化(ハッシュ結合、再フェッチ、直接経路読み込み、記憶指数、適応計画、…)で特に完全なテーブルスキャンが最も効率的であるかもしれないと認識しました.何かが文脈なしで非効率であると決して言わないでください、あなたはそれの最高を逃します.文脈のない「ベストプラクティス」が広がると神話になる.DynamoDBは、単純な(アクセス経路が制限され、コストベースのオプティマイザがない)という利点があります.そして、アクセスパスのコストを理解するのは簡単です.
    どのように効率を測定するのですか?あなたが1つのRCUで得ることができるアイテムの数を見るとき、スキャンは実際に最も効率的です.そして、どうか、我々はもう一つの操作がより効率的でありえるように、「避ける」ことをスキャンしなければならないと思いません.我々がDynamOBを避けるべきは、重要な操作のためにスキャンを必要とするデータモデルです.キー値のデータストアであることを覚えておいてください.一つのハッシュキー値に対して1つのアイテムを取得するために最適化されています.多くのアイテムを読む必要があるときは、一つのテーブルデザインでは、1つのRCUすべての項目を結合するか、グローバルなセカンダリインデックスを使用して別のパーティションスキーマを持つレプリカとして取得することができます.しかし、すべてのパーティションから読むとすぐに、スキャンは最も効率的な操作です.