Python의 DynamoDB Query (boto3)

이 페이지가 짧은 이유는 boto3의 query 페이지네이터에 있습니다. LastEvaluatedKey를 완전히 감춰 주기 때문입니다. 대신 여러분이 원했을 법한 숫자 하나도 함께 감추는데, 그것을 믿기 전에 알아 둘 가치가 있는 부분입니다. 애초에 query를 언제 써야 하는지는 Query vs. Scan을 보세요.

코드

import boto3

client = boto3.client("dynamodb")

paginator = client.get_paginator("query")

items = []
for page in paginator.paginate(
    TableName="Music",
    KeyConditionExpression="#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)",
    ExpressionAttributeNames={"#hashKey": "Artist", "#rangeKey": "SongTitle"},
    ExpressionAttributeValues={":hashKeyValue": {"S": "Arturo Sandoval"}, ":rangeKeyValue": {"S": "C"}},
):
    items.extend(page["Items"])

print(f"Found {len(items)} items")

페이지네이터는 요금을 합산해 주지 않습니다

곡마다 약 3.9 KB이고 모두 Artist = "Arturo Sandoval" 아래에 있는 600곡짜리 픽스처에 대해, 이 루프는 271개, 271개, 58개짜리 세 페이지를 내놓으며 각각 128.5, 128.5, 27.5 읽기 단위를 씁니다. 같은 페이지네이터에 병합된 결과 하나를 요청하면 이렇게 나옵니다:

build_full_result() -> Items 600  Count 600  ScannedCount 600
                       ConsumedCapacity.CapacityUnits 128.5

CountScannedCount는 합산되었습니다. ConsumedCapacity는 아닙니다. 이것은 첫 페이지의 수치이고, 실제 합계는 284.5였습니다. botocore의 DynamoDB 페이지네이터 설정이 그 이유를 분명히 밝힙니다. CountScannedCount는 결과 키로, ConsumedCapacity는 비집계 키로 등록되어 있습니다. build_full_result()에서 용량을 로그로 남기고 있다면, 파티션 전체 읽기를 절반 이상 낮춰 보고하고 있는 것입니다.

위의 for page in paginator.paginate(...) 루프에서 나오는 페이지별 딕셔너리는 원시 응답이므로, page["ConsumedCapacity"]["CapacityUnits"]를 직접 합산하면 정직한 284.5가 나옵니다.

왕복을 58번 더 하게 만드는 Limit

Limit은 유효한 query 파라미터라 paginate()가 받아들이지만, Python 사용자가 기대하는 그 파라미터가 아닙니다:

paginate(..., Limit=10)  ->  61 pages, 10 items each
paginate(...)            ->   3 pages

이것은 전체가 아니라 요청당 항목 수를 제한하므로, 페이지네이터는 같은 600개 항목을 가져오려고 성실하게 HTTP 호출을 61번 합니다. 총량을 제한하려면 PaginationConfig={"MaxItems": 10}을 쓰세요. Limit에 대응하는 손잡이는 PaginationConfig["PageSize"]입니다.

2026-07-28에 CPython 3.14.6의 boto3 1.43.58로 DynamoDB Local(amazon/dynamodb-local)에 대해 측정했습니다.

설명

  • 클라이언트는 양방향 모두 DynamoDB JSON으로 말합니다. 값은 {"S": "Arturo Sandoval"} 형태로 들어가고 Year{"N": "1994"}로 돌아옵니다. 리소스 API(boto3.resource("dynamodb").Table(...).query)는 양방향으로 변환해 Decimal('1994')를 건네줍니다. 금액에는 올바른 선택이지만, float와 더해지기를 거부하는 것을 처음 보면 당황스럽습니다.
  • Key("Artist").eq(...)는 리소스 API 전용입니다. 클라이언트에 넘기면 요청이 나가기도 전에 예외가 납니다: ParamValidationError: Invalid type for parameter KeyConditionExpression ... valid types: <class 'str'>. 클라이언트가 원하는 것은 이 페이지가 만드는 표현식 문자열입니다.
  • 키 조건은 등호 하나에 정렬 키 비교 최대 하나(=, <, <=, >, >=, BETWEEN, begins_with)입니다. 그 밖의 것은 FilterExpression에 넣으세요. boto3는 그대로 전달하고 DynamoDB는 읽은 뒤에 적용합니다. ScanIndexForward=False는 순서를 뒤집고, IndexName="..."은 인덱스로 대상을 바꿉니다.

시각적으로 해보기

DynamoDB Expression Builder는 키 조건과 타입이 지정된 ExpressionAttributeValues 맵을 boto3에 바로 쓸 수 있는 Python으로 작성해 줍니다. {"N": "2010"} 대신 {"N": 2010}을 타이핑할 때 어긋나는 바로 그 부분입니다.

키 조건 폼에서 같은 쿼리를 여러분의 테이블로 겨냥하고 결과를 페이지 단위 그리드로 읽으려면 DynoTable을 다운로드하세요.

관련 가이드

참고 자료

이 요청을 시각적으로 만들기

무료 DynamoDB 쿼리 빌더에서 이 작업을 구성하세요 — 키 조건, 필터, 인덱스, Limit, 정렬 순서, 페이지네이션 루프 — 그리고 실행 가능한 SDK v3, CLI, boto3 프로그램으로 다시 복사하세요.

DynamoDB 쿼리 빌더 열기

Console 없이 DynamoDB 작업하기

DynamoDB로는 실행할 수 없는 진짜 SQL(JOINs, GROUP BY, 집계)을 실행하는 빠른 DynamoDB 데스크톱 클라이언트. 시각적 편집과 여러분 자신의 Bedrock 키로 동작하는 AI 에이전트를 제공합니다.

30일 무료 체험, 신용카드 불필요 — 이후 기간 제한 없는 무료 요금제.