DynamoDBテーブルで複数のデータを取得するとき、効率よくデータを取得することを意識します。 これらは、パーティションキーやソートキーの設計、GSIの設計などに関係します。
そこで、ふと気になりました。
「query()
のソートキーに条件を指定したら、読み込みキャパシティーユニットは減るのだろうか?」と。
試してみました。
おすすめの方
- boto3でDynamoDBテーブルの
query()
を利用したい方 query()
のソートキーに条件を指定したととき、読み込みキャパシティーユニットが減るのか知りたい方
実験用の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の設計が大事だと実感しました。