DynamoDBの query() でソートキーに条件を指定したとき、読み込みキャパシティーユニットが減るのか試してみた

DynamoDBテーブルからデータ取得するとき、 query() のソートキーに条件を指定すると読み込みキャパシティーユニットは減るか気になったので、試してみました。
2023.02.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

DynamoDBテーブルで複数のデータを取得するとき、効率よくデータを取得することを意識します。 これらは、パーティションキーやソートキーの設計、GSIの設計などに関係します。

そこで、ふと気になりました。

query()のソートキーに条件を指定したら、読み込みキャパシティーユニットは減るのだろうか?」と。

試してみました。

おすすめの方

  • boto3でDynamoDBテーブルのquery()を利用したい方
  • query()のソートキーに条件を指定したととき、読み込みキャパシティーユニットが減るのか知りたい方

実験用のDynamoDBテーブルを作成して、データを格納する

DynamoDBテーブルを作成する

DynamoDBテーブルを作成する

実験用のデータを格納する

次のスクリプトを実行してDynamoDBテーブルにデータを格納します。

put_item.py

import boto3

def main():
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('device-data-table')

    with table.batch_writer() as batch:
        for i in range(1000):
            batch.put_item(Item={
                'deviceId': 'device1',
                'timestamp': i,
                'message': f'hello {i}',
            })

    with table.batch_writer() as batch:
        for i in range(1000):
            batch.put_item(Item={
                'deviceId': 'device2',
                'timestamp': i * 2,
                'message': f'hello {i * 2}',
            })


if __name__=='__main__':
    main()
python put_item.py

query() を実行して、読み込みキャパシティーユニットを把握する

スクリプト

2つのquery()を実行して比較します。

  • パーティションキーを指定する
  • パーティションキーを指定し、ソートキーで範囲を指定する

それぞれの結果について、次の3つの情報を取得します。

  • Count
    • 実際のデータ数
  • ScannedCount
    • フィルター適応前のデータ数
  • ConsumedCapacity
    • 消費したキャパシティーユニット

query.py

import boto3
from boto3.dynamodb.conditions import Key

def main():
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('device-data-table')


    query_normal(table, 'device1')

    query_sk(table, 'device1', 200)



def query_normal(table, device_id):
    options = {
        'KeyConditionExpression': Key('deviceId').eq(device_id),
        'ReturnConsumedCapacity': 'TOTAL',
    }

    while True:
        resp = table.query(**options)

        print(f'Count: {resp["Count"]}, ScannedCount: {resp["ScannedCount"]}, ConsumedCapacity: {resp["ConsumedCapacity"]}')

        if 'LastEvaluatedKey' not in resp:
            break
        options['ExclusiveStartKey'] = resp['LastEvaluatedKey']



def query_sk(table, device_id, timestamp):
    options = {
        'KeyConditionExpression': Key('deviceId').eq(device_id) & Key('timestamp').lt(timestamp),
        'ReturnConsumedCapacity': 'TOTAL',
    }

    while True:
        resp = table.query(**options)

        print(f'Count: {resp["Count"]}, ScannedCount: {resp["ScannedCount"]}, ConsumedCapacity: {resp["ConsumedCapacity"]}')

        if 'LastEvaluatedKey' not in resp:
            break
        options['ExclusiveStartKey'] = resp['LastEvaluatedKey']


if __name__=='__main__':
    main()
python query.py

実行結果

取得したデータ数、および、読み込みキャパシティーユニット量のそれぞれに違いがあります。 つまり、ソートキーに条件を指定すると、読み込みキャパシティーユニット量が減りました。

Count: 1000, ScannedCount: 1000, ConsumedCapacity: {'TableName': 'device-data-table', 'CapacityUnits': 5.5}
Count: 200, ScannedCount: 200, ConsumedCapacity: {'TableName': 'device-data-table', 'CapacityUnits': 1.5}

おまけ

せっかくなので、query()でフィルターを指定してみました。

omake.py

import boto3
from boto3.dynamodb.conditions import Attr, Key

def main():
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('device-data-table')

    query_filter(table, 'device1', 200)


def query_filter(table, device_id, timestamp):
    options = {
        'KeyConditionExpression': Key('deviceId').eq(device_id) & Key('timestamp').lt(timestamp),
        'FilterExpression': Attr('message').contains('11'),
        'ReturnConsumedCapacity': 'TOTAL',
    }

    while True:
        resp = table.query(**options)

        print(f'Count: {resp["Count"]}, ScannedCount: {resp["ScannedCount"]}, ConsumedCapacity: {resp["ConsumedCapacity"]}')

        if 'LastEvaluatedKey' not in resp:
            break
        options['ExclusiveStartKey'] = resp['LastEvaluatedKey']


if __name__=='__main__':
    main()
python omake.py

フィルターは、取得したデータに対して結果を返す前に適用されることが分かります。

Count: 11, ScannedCount: 200, ConsumedCapacity: {'TableName': 'device-data-table', 'CapacityUnits': 1.5}

さいごに

あらためて、DynamoDBテーブルのパーティションキーとソートキー、およびGSIの設計が大事だと実感しました。

参考