Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

DynamoDBについて調べてみたことを書いてみた その3

前回書いた記事「DynamoDBについて調べてみたことを書いてみた その1、その2」の続きの話です。

kodak.hatenablog.com

kodak.hatenablog.com

今回は、いよいよLSIとGSIについての話です。

LSIとGSIとは?

LSIとGSIは、それぞれの正式名称は以下になります。

そう、インデックスなんです!!
ですが、ただのインデックスではありません。これらはインデックスですが、テーブルを複製しています。
何を言っているかわからないと思いますが、LSIから見ていきます。

LSI(ローカル・セカンダリ・インデックス)

DynamoDBの一意となるKey(プライマリーキー)は、Hash、HashとRangeの組み合わせの2つから作ります。
しかし、それでは使いづらい・・・
何故ならテーブル内に、他にも一意になりそうなKey項目や検索条件で使用したい項目があっても、今のままでは指定する事ができないからです。そんなときに使うのがGSIとLSIです。

LSIは、テーブルに対して最初に指定したHashとRangeの組み合わせに対して、もう1つ別のRangeを設定できます。 (ただし、テーブル作成時にHashとRangeの両方を定義した場合に限ります。)
つまり、検索条件やソートしたい項目を追加で1つ選ぶ事ができるのがLSIの魅力です。

実際にテーブルを作成するコマンドを見てみましょう。

aws dynamodb create-table \
    --table-name Music2 \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
        AttributeName=SongWriter,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=1,WriteCapacityUnits=1 \
    --local-secondary-indexes \
        "IndexName=Music-Lsi,
            KeySchema=[
                {AttributeName=Artist,KeyType=HASH},
                {AttributeName=SongWriter,KeyType=RANGE}
            ],Projection={ProjectionType=KEYS_ONLY}"
  • --local-secondary-indexes : LSIを定義する。
  • IndexName : LSIの名前を定義する。
  • KeySchema-[{AttributeName=XXX,KeyType=HASH}] : LSIのHashを定義する。--key-schemaのHASHと同じになる。
  • KeySchema-[{AttributeName=XXX,KeyType=RANGE}] : LSIのHashに対して、新しいRangeを指定する。
  • Projection : インデックスに射影される属性を指定する。

ちなみに、LSIのHashを既存テーブルのHash以外を選ぶと以下のように怒られます。

An error occurred (ValidationException) when calling the CreateTable operation: Local Secondary indices must have the same hash key as the main table

上記で「ただのインデックスではありません。これらはインデックスですが、テーブルを複製します。」と言いました。
LSIは、ProjectionTypeで指定した項目+既存テーブルのHashとRangeを持つ別テーブルを作成します。
それらのテーブルのデータは、既存テーブルのHash毎に格納されます。
図で書くとこんな感じです。

f:id:special-moucom:20190331220857p:plain

なんかインデックスっぽくないですよね(= =;;)

GSI(グローバル・セカンダリ・インデックス)

LSIはHashは変えずに、新しいRangeを追加していました。
HashもRangeも既存テーブルとは異なる項目を設定したい!そんなときに使うのが、GSIです。

テーブルを作成するコマンドを見てみましょう。

aws dynamodb create-table \
    --endpoint-url http://localhost:8000 \
    --table-name Music4 \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
        AttributeName=Label,AttributeType=S \
        AttributeName=SongWriter,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=1,WriteCapacityUnits=1 \
    --global-secondary-indexes \
        "IndexName=Music-Gsi,
            KeySchema=[
                {AttributeName=Label,KeyType=HASH},
                {AttributeName=SongWriter,KeyType=RANGE}
            ],
            Projection={ProjectionType=KEYS_ONLY},
            ProvisionedThroughput={ ReadCapacityUnits=1, WriteCapacityUnits=1 }"
  • --global-secondary-indexes : GSIを定義する。
  • IndexName : GSIの名前を定義する。
  • KeySchema-[{AttributeName=XXX,KeyType=HASH}] : GSIのHashを定義する。--key-schemaのHASHと異なる項目を設定する。
  • KeySchema-[{AttributeName=XXX,KeyType=RANGE}] : GSIのHashに対して、Rangeを指定する。
  • Projection : インデックスに射影される属性を指定する。
  • ProvisionedThroughput : 指定したテーブルのスループットを指定します。(こちらの説明はまた今度)

LSIと書き方はほぼ同じです。とはいえ、GSIで指定するHASHに制限はありません。

LSI同様、インデックスでありながらGSIもテーブルを複製しています。しかし、恐らくデータの格納方法はLSIとの違うように思えます。
LSIは、既存のテーブルのHash毎にデータを格納していましたが、GSIは完全に別テーブル、別Hashを使っているように見えるからです。
図で書くとこんな感じです。

f:id:special-moucom:20190331220934p:plain

GSIで指定するHashですが、これがどこまで一意を気にしないといけないのか・・・私もわかっていません。
ですが、あくまでインデックスなので、既存テーブルのHashを何かしらうまいこと使ってくれているようにも思えます。 ここら辺はもう少し調査が必要ですね。(= =;;)

LSIとGSIの使いどころ

LSIとGSIの使いどころですが、単純にRange=SortKeyと考えると別の項目でソートしたい場合、既存のテーブルに対して、色々な角度からテーブルを参照したい場合(例えば、テーブルに曲名とジャンルの項目がある場合、曲名からテーブルを見たり、ジャンルからテーブルを見たり等)に使えそうです。
調べてみると、LSIよりもGSIの方が使い勝手が良く、GSIをいかに上手く使うのかがDynamoDBのポイントだそうです。

最後に・・・

DynamoDBについて、個人的に調べていた事を3回に渡って書いてみました。
私自身、DynamoDBの使用経験はありません。(少しコマンドで叩いて使ってみた程度です。)
そんな状態で調べた事をずらずらと書いたので、間違った情報もきっと幾つかあると思います。

とはいえ、ブログに書く事で色々と学びになりましたし、整理もできました。結果的にやってよかったと思っています。

現在、LambdaからDBを扱うならDynamoDB一択ですが、もう一つDocumentDB(MongoDB互換)というのがあります。

aws.amazon.com

こちらは東京リージョンにはまだ来ていないのですが、噂ではこちらもLambdaとの相性が良く、DynamoDBの代わりになるDBになる様です。
こちらも東京リージョンにきた時には色々と調べてみたいなぁと思います。