前回書いた記事「DynamoDBについて調べてみたことを書いてみた その1、その2」の続きの話です。
今回は、いよいよ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毎に格納されます。
図で書くとこんな感じです。
なんかインデックスっぽくないですよね(= =;;)
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を使っているように見えるからです。
図で書くとこんな感じです。
GSIで指定するHashですが、これがどこまで一意を気にしないといけないのか・・・私もわかっていません。
ですが、あくまでインデックスなので、既存テーブルのHashを何かしらうまいこと使ってくれているようにも思えます。
ここら辺はもう少し調査が必要ですね。(= =;;)
LSIとGSIの使いどころ
LSIとGSIの使いどころですが、単純にRange=SortKeyと考えると別の項目でソートしたい場合、既存のテーブルに対して、色々な角度からテーブルを参照したい場合(例えば、テーブルに曲名とジャンルの項目がある場合、曲名からテーブルを見たり、ジャンルからテーブルを見たり等)に使えそうです。
調べてみると、LSIよりもGSIの方が使い勝手が良く、GSIをいかに上手く使うのかがDynamoDBのポイントだそうです。
最後に・・・
DynamoDBについて、個人的に調べていた事を3回に渡って書いてみました。
私自身、DynamoDBの使用経験はありません。(少しコマンドで叩いて使ってみた程度です。)
そんな状態で調べた事をずらずらと書いたので、間違った情報もきっと幾つかあると思います。
とはいえ、ブログに書く事で色々と学びになりましたし、整理もできました。結果的にやってよかったと思っています。
現在、LambdaからDBを扱うならDynamoDB一択ですが、もう一つDocumentDB(MongoDB互換)というのがあります。
こちらは東京リージョンにはまだ来ていないのですが、噂ではこちらもLambdaとの相性が良く、DynamoDBの代わりになるDBになる様です。
こちらも東京リージョンにきた時には色々と調べてみたいなぁと思います。