SJ blog
database
A

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

DynamoDB GSI vs LSI — インデックスの仕組み・制約・設計パターン

DynamoDBのGSI(グローバルセカンダリインデックス)とLSI(ローカルセカンダリインデックス)の違い、作成タイミング、一貫性、スループット設定、クエリパターンを解説。

一言結論

GSIはテーブル作成後も追加でき柔軟性が高い一方でWCUが独立しているためボトルネックになりやすく、LSIは強整合性読み取りが可能だがテーブル作成時のみ追加でき1パーティション10GBの制限があるため、現代設計ではGSI中心のSingle Table Designが推奨される。

DynamoDBのインデックス

DynamoDBはキーバリュー型のNoSQLだが、セカンダリインデックスを使うことでパーティションキー以外の属性でもクエリできる。

特徴GSILSI
作成タイミングテーブル作成後でも追加可能テーブル作成時のみ
PKの制約プライマリテーブルと異なるPKを設定可能プライマリテーブルと同じPKが必須
SKの制約任意必須(異なる属性)
スループット独自のRCU/WCUを設定テーブルと共有
強整合性読み取り不可(結果整合性のみ)可能
パーティションサイズ無制限10GBの制限あり
最大数20個/テーブル5個/テーブル

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

同じパーティション内でソートキーを変えて検索するためのインデックス。

# テーブル定義(LSIあり)
import boto3

dynamodb = boto3.client('dynamodb')

dynamodb.create_table(
    TableName='Orders',
    KeySchema=[
        {'AttributeName': 'userId', 'KeyType': 'HASH'},     # PK
        {'AttributeName': 'orderId', 'KeyType': 'RANGE'}    # SK
    ],
    AttributeDefinitions=[
        {'AttributeName': 'userId', 'AttributeType': 'S'},
        {'AttributeName': 'orderId', 'AttributeType': 'S'},
        {'AttributeName': 'orderDate', 'AttributeType': 'S'},  # LSI用
        {'AttributeName': 'totalAmount', 'AttributeType': 'N'}  # LSI用
    ],
    LocalSecondaryIndexes=[
        {
            'IndexName': 'userId-orderDate-index',  # 日付でソートするLSI
            'KeySchema': [
                {'AttributeName': 'userId', 'KeyType': 'HASH'},      # テーブルと同じPK
                {'AttributeName': 'orderDate', 'KeyType': 'RANGE'}   # 異なるSK
            ],
            'Projection': {'ProjectionType': 'ALL'}
        }
    ],
    BillingMode='PAY_PER_REQUEST'
)
# LSIを使ったクエリ(特定ユーザーの注文を日付順に取得)
from boto3.dynamodb.conditions import Key

table = dynamodb_resource.Table('Orders')
response = table.query(
    IndexName='userId-orderDate-index',
    KeyConditionExpression=Key('userId').eq('user123'),
    ScanIndexForward=False  # 降順(新しい順)
)

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

テーブルの任意の属性をPKとして設定できる。テーブルとは完全に異なるアクセスパターンをサポートする。

# GSIの追加(テーブル作成後に追加可能)
dynamodb.update_table(
    TableName='Orders',
    AttributeDefinitions=[
        {'AttributeName': 'status', 'AttributeType': 'S'},
        {'AttributeName': 'orderDate', 'AttributeType': 'S'}
    ],
    GlobalSecondaryIndexUpdates=[
        {
            'Create': {
                'IndexName': 'status-orderDate-index',
                'KeySchema': [
                    {'AttributeName': 'status', 'KeyType': 'HASH'},     # テーブルと異なるPK
                    {'AttributeName': 'orderDate', 'KeyType': 'RANGE'}
                ],
                'Projection': {'ProjectionType': 'INCLUDE',
                               'NonKeyAttributes': ['userId', 'totalAmount']},
                'ProvisionedThroughput': {
                    'ReadCapacityUnits': 5,
                    'WriteCapacityUnits': 5
                }
            }
        }
    ]
)
# GSIを使ったクエリ(ステータスが"processing"の注文を日付順に取得)
response = table.query(
    IndexName='status-orderDate-index',
    KeyConditionExpression=Key('status').eq('processing') &
                           Key('orderDate').begins_with('2026-04')
)

Sparse Index(スパースインデックス)パターン

GSIは属性が存在するアイテムのみインデックスに含まれる。これを利用して「特定の属性を持つアイテムのみ」を効率的に検索できる。

# unprocessedというフラグがあるアイテムのみGSIに含める
# フラグが削除されたらGSIから自動的に除外される

# 未処理の注文のみを高速に取得
{
  "orderId": "order-123",
  "status": "processing",
  "unprocessed": "YES"   ← この属性があるアイテムのみGSIに含まれる
}

# 処理完了後にunprocessedを削除 → GSIから自動除外

Projection(射影)の設計

ProjectionType:
  ALL:        テーブルの全属性をインデックスにコピー(ストレージ多め)
  KEYS_ONLY:  プライマリキーとインデックスキーのみ
  INCLUDE:    指定した属性のみ

設計のポイント:
  - クエリで必要な属性をPROJECTIONに含める
  - ALLは汎用的だがストレージコストが高い
  - 不足した属性はベーステーブルを再度読む必要がある(追加コスト)

GSIのスロットリング

GSIはプライマリテーブルへの書き込み時に同時に更新される。GSIのWCUが不足するとテーブル全体の書き込みがスロットリングされる。

テーブルのWCU: 100
GSIのWCU:    10  ← ボトルネック

→ テーブルへの書き込みが100WCU以内でもGSIが10WCUを超えると
  テーブル全体の書き込みがスロットリングされる

On-demand モードではGSIも自動スケールするため、このリスクが軽減される。

試験頻出ポイント

シナリオ回答
テーブル作成後にインデックスを追加したいGSI(LSIはテーブル作成時のみ)
強整合性読み取りが必要LSI(GSIは結果整合性のみ)
テーブルと異なるPKでクエリしたいGSI
同じPKで異なるSKでのクエリLSI
パーティション内の10GB制限があるLSI(1パーティション = 1PKの値で10GB上限)
GSIの書き込みボトルネックGSIのWCUを適切に設定、またはOn-demand使用

まとめ

GSIは柔軟性が高くテーブル作成後も追加可能で、異なるアクセスパターンに対応できる。LSIはテーブル作成時のみ設定可能で制約が多いが、強整合性読み取りが可能な点が特徴だ。現代の設計ではGSIを中心に設計し、Single Table Design パターンで1テーブルに多様なアクセスパターンを収めることが推奨される。