

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 Python 和 Boto3 為 Amazon DynamoDB 編寫程式
<a name="programming-with-python"></a>

本指南為想要透過 Python 使用 Amazon DynamoDB 的程式設計人員提供指導。了解不同的抽象層、組態管理、錯誤處理、控制重試政策、管理保持連線等。

**Topics**
+ [關於 Boto](#programming-with-python-about)
+ [使用 Boto 文件](#programming-with-python-documentation)
+ [了解用戶端和資源抽象層](#programming-with-python-client-resource)
+ [使用資料表資源 batch\$1writer](#programming-with-python-batch-writer)
+ [探索用戶端和資源層的其他程式碼範例](#programming-with-python-additional-code)
+ [了解用戶端和資源物件如何與工作階段和執行緒互動](#programming-with-python-sessions-thread-safety)
+ [自訂 Config 物件](#programming-with-python-config)
+ [錯誤處理](#programming-with-python-error-handling)
+ [日誌](#programming-with-python-logging)
+ [事件勾點](#programming-with-python-event-hooks)
+ [分頁和分頁程式](#programming-with-python-pagination)
+ [等待程式](#programming-with-python-waiters)

## 關於 Boto
<a name="programming-with-python-about"></a>

您可以使用適用於 Python 的官方 AWS SDK 從 Python 存取 DynamoDB，通常稱為 **Boto3**。Boto (發音為 boh-toh) 的名稱來自亞馬遜河原生的淡水海豚。Boto3 程式庫是程式庫的第三個主要版本，於 2015 年首次發行。Boto3 程式庫相當大，因為它支援所有 AWS 服務，而不只是 DynamoDB。此指導僅針對 Boto3 中與 DynamoDB 相關的部分。

Boto 由 維護並發佈 AWS 為 GitHub 上託管的開放原始碼專案。分為兩個套件：[Botocore](https://github.com/boto/botocore) 和 [Boto3](https://github.com/boto/boto3)。
+ **Botocore** 提供低階功能。在 Botocore 中，您會找到用戶端、工作階段、憑證、組態和例外類別。
+ **Boto3** 建置在 Botocore 之上，提供更高階、更具 Python 風格的介面。具體而言，它公開 DynamoDB 資料表做為資源，並提供比服務導向的低階用戶端介面更簡單、更優雅的介面。

由於這些專案託管在 GitHub 上，因此您可以檢視原始程式碼、追蹤未解決的問題，或提交您自己的問題。

## 使用 Boto 文件
<a name="programming-with-python-documentation"></a>

使用下列資源開始使用 Boto 文件：
+ 從 [Quickstart 區段](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html)開始，該區段提供安裝套件的堅實起點。如需安裝 Boto3 的說明，請前往此處 (Boto3 通常可在 等 AWS 服務內自動使用 AWS Lambda)。
+ 之後，請專注於文件的 [DynamoDB 指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/dynamodb.html)。它說明如何執行基本的 DynamoDB 活動：建立和刪除資料表、操作項目，以及執行批次操作、查詢和掃描。其範例使用**資源**介面。當您看到 `boto3.resource('dynamodb')`，表示您正在使用較高階的**資源**介面。
+ 在本指南之後，您可以檢閱 [DynamoDB 參考](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html)。此登陸頁面提供您可使用的類別和方法的完整清單。在頂端，您會看到 `DynamoDB.Client` 類別，提供所有控制平面和資料平面操作的低階存取。在底部則可查看 `DynamoDB.ServiceResource` 類別，這是更高階且具有 Python 風格的介面。您可以使用它來建立資料表、跨資料表執行批次操作，或取得資料表特定動作的 `DynamoDB.ServiceResource.Table` 執行個體。

## 了解用戶端和資源抽象層
<a name="programming-with-python-client-resource"></a>

您將使用的兩個介面是**用戶端**介面和資源****介面。
+ 低階**用戶端**介面提供對基礎服務 API 的 1 對 1 映射。DynamoDB 提供的每個 API 都可以透過用戶端取得。這表示用戶端介面可以提供完整的功能，但使用起來通常更冗長且複雜。
+ 較高階的**資源**介面不提供基礎服務 API 的 1 對 1 映射。不過，它提供可讓您更方便存取服務的方法，例如 `batch_writer`。

以下是使用用戶端介面插入項目的範例。請注意所有的值如何以映射形式傳遞，其中鍵代表其類型 (字串為「S」，數字為「N」)，值則為字串。這稱為 DynamoDB JSON 格式。

```
import boto3

dynamodb = boto3.client('dynamodb')

dynamodb.put_item(
    TableName='YourTableName',
    Item={
        'pk': {'S': 'id#1'},
        'sk': {'S': 'cart#123'},
        'name': {'S': 'SomeName'},
        'inventory': {'N': '500'},
        # ... more attributes ...
    }
)
```

以下是使用資源介面的相同 `PutItem` 操作。資料類型是隱含的：

```
import boto3

dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')

table.put_item(
    Item={
        'pk': 'id#1',
        'sk': 'cart#123',
        'name': 'SomeName',
        'inventory': 500,
        # ... more attributes ...
    }
)
```

如有需要，您可以使用 boto3 提供的 `TypeSerializer` 和 `TypeDeserializer` 類別，在一般 JSON 和 DynamoDB JSON 之間轉換：

```
def dynamo_to_python(dynamo_object: dict) -> dict:
    deserializer = TypeDeserializer()
    return {
        k: deserializer.deserialize(v) 
        for k, v in dynamo_object.items()
    }  
  
def python_to_dynamo(python_object: dict) -> dict:
    serializer = TypeSerializer()
    return {
        k: serializer.serialize(v)
        for k, v in python_object.items()
    }
```

以下是如何使用用戶端介面執行查詢。它將查詢表達為 JSON 建構，使用需要變數替換的 `KeyConditionExpression` 字串來處理任何潛在的關鍵字衝突：

```
import boto3

client = boto3.client('dynamodb')

# Construct the query
response = client.query(
    TableName='YourTableName',
    KeyConditionExpression='pk = :pk_val AND begins_with(sk, :sk_val)',
    FilterExpression='#name = :name_val',
    ExpressionAttributeValues={
        ':pk_val': {'S': 'id#1'},
        ':sk_val': {'S': 'cart#'},
        ':name_val': {'S': 'SomeName'},
    },
    ExpressionAttributeNames={
        '#name': 'name',
    }
)
```

使用資源介面的相同查詢操作可以縮短和簡化：

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

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('YourTableName')

response = table.query(
    KeyConditionExpression=Key('pk').eq('id#1') & Key('sk').begins_with('cart#'),
    FilterExpression=Attr('name').eq('SomeName')
)
```

最後，假設您想要取得資料表的大致大小 (這是保留在資料表上的中繼資料，大約每 6 小時更新一次)。使用用戶端介面，您可以執行 `describe_table()` 操作，並從傳回的 JSON 結構提取答案：

```
import boto3

dynamodb = boto3.client('dynamodb')

response = dynamodb.describe_table(TableName='YourTableName')
size = response['Table']['TableSizeBytes']
```

透過資源介面，資料表會隱含地執行描述操作，並直接將資料顯示為屬性：

```
import boto3

dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')
size = table.table_size_bytes
```

**注意**  
考慮使用用戶端或資源界面進行開發時，請注意，根據[資源文件](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html)，不會將新功能新增至資源界面：「 AWS Python SDK 團隊不打算將新功能新增至 boto3 中的資源界面。在 boto3 的生命週期期間，現有的介面將繼續運作。客戶可以透過用戶端介面尋找存取較新服務功能。」

## 使用資料表資源 batch\$1writer
<a name="programming-with-python-batch-writer"></a>

`batch_writer` 是一種便利的方法，僅適用於較高階的資料表資源。DynamoDB 支援批次寫入操作，在一個網路請求中最多允許 25 個放置或刪除操作。像這樣的批次處理可最大限度減少網路往返，從而提高效率。

透過低階用戶端程式庫，您可以使用 `client.batch_write_item()` 操作來執行批次。您必須手動將工作分成 25 個批次。在每次操作之後，您也必須請求獲得未處理項目的清單 (有些寫入操作可能會成功，有些則可能會失敗)。然後，您必須將這些未處理的項目再次傳遞至稍後的 `batch_write_item()` 操作。有大量的樣板程式碼。

[Table.batch\$1writer](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/table/batch_writer.html) 方法會建立內容管理員，用於以批次方式寫入物件。它會顯示一個介面，看起來像是您一次寫入一個項目，但其實在內部會分批緩衝和傳送項目。它也會隱含地處理未處理的項目重試。

```
dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')

movies = # long list of movies in {'pk': 'val', 'sk': 'val', etc} format
with table.batch_writer() as writer:
    for movie in movies:
        writer.put_item(Item=movie)
```

## 探索用戶端和資源層的其他程式碼範例
<a name="programming-with-python-additional-code"></a>

您也可以參考下列程式碼範例儲存庫，以探索在使用用戶端和資源的情況下，各種函數的使用方式：
+ [官方 AWS 單一動作程式碼範例。](https://docs.aws.amazon.com/code-library/latest/ug/python_3_dynamodb_code_examples.html)
+ [官方 AWS 案例導向程式碼範例。](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python)
+ [社群維護的單一動作程式碼範例。](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/python)

## 了解用戶端和資源物件如何與工作階段和執行緒互動
<a name="programming-with-python-sessions-thread-safety"></a>

資源物件不具備執行緒安全性，不應跨執行緒或程序共用。如需詳細資訊，請參閱[資源指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html#multithreading-or-multiprocessing-with-resources)。

相反地，用戶端物件通常具備執行緒安全性，但特定進階功能除外。如需更多詳細資訊，請參閱[用戶端指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html#multithreading-or-multiprocessing-with-clients)。

工作階段物件不具備執行緒安全性。因此，每次在多執行緒環境中建立用戶端或資源時，應先建立新的工作階段，然後從工作階段建立用戶端或資源。如需詳細資訊，請參閱[工作階段指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/session.html#multithreading-or-multiprocessing-with-sessions)。

當您呼叫 `boto3.resource()` 時，您會隱含地使用預設工作階段。這有助於撰寫單執行緒程式碼。撰寫多執行緒程式碼時，您會想要先為每個執行緒建構新的工作階段，然後從該工作階段擷取資源：

```
# Explicitly create a new Session for this thread 
session = boto3.Session()
dynamodb = session.resource('dynamodb')
```

## 自訂 Config 物件
<a name="programming-with-python-config"></a>

建構用戶端或資源物件時，您可以傳遞選用的已命名參數來自訂行為。名為 `config` 的參數會解鎖各種功能。這是 `botocore.client.Config` 的執行個體，而 [Config 參考文件](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html)會顯示它公開供您控制的所有內容。[組態指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html)提供良好的概觀。

**注意**  
您可以修改在工作階段層級、 AWS 組態檔案內或作為環境變數的許多行為設定。

**逾時的組態**

自訂組態的一個用途是調整聯網行為：
+ **connect\$1timeout (浮點數或整數)** – 嘗試建立連線時擲出逾時例外狀況的秒數。預設值為 60 秒。
+ **read\$1timeout (浮點數或整數)** – 嘗試從連線讀取時擲出逾時例外狀況前的秒數。預設值為 60 秒。

對 DynamoDB 而言，60 秒的逾時太長了。這表示暫時性網路故障會導致用戶端延遲一分鐘，然後才可以再試一次。下列程式碼會將逾時縮短為一秒：

```
import boto3
from botocore.config import Config

my_config = Config(
   connect_timeout = 1.0,
   read_timeout = 1.0
)
dynamodb = boto3.resource('dynamodb', config=my_config)
```

如需逾時的更多討論，請參閱[調整延遲感知 DynamoDB 應用程式的 AWS Java SDK HTTP 請求設定](https://aws.amazon.com/blogs/database/tuning-aws-java-sdk-http-request-settings-for-latency-aware-amazon-dynamodb-applications/)。請注意，Java SDK 具有比 Python 更多的逾時組態。

**保持連線的組態**

如果您使用的是 botocore 1.27.84 或更新版本，您也可以控制 **TCP Keep-Alive**：
+ **tcp\$1keepalive** (bool) - 啟用 TCP Keep-Alive 通訊端選項，如果設定為 `True` (預設為 `False`)，則在建立新連線時使用。從 botocore 1.27.84 開始才能使用。

將 TCP Keep-Alive 設定為 `True` 可減少平均延遲。以下是當您有正確的 botocore 版本時，將 TCP Keep-Alive 設定為 true 的範例程式碼：

```
import botocore
import boto3
from botocore.config import Config
from distutils.version import LooseVersion

required_version = "1.27.84"
current_version = botocore.__version__

my_config = Config(
   connect_timeout = 0.5,
   read_timeout = 0.5
)
if LooseVersion(current_version) > LooseVersion(required_version):
    my_config = my_config.merge(Config(tcp_keepalive = True))

dynamodb = boto3.resource('dynamodb', config=my_config)
```

**注意**  
TCP Keep-Alive 與 HTTP Keep-Alive 不同。使用 TCP Keep-Alive 時，基礎作業系統會透過通訊端連線傳送小型封包，以保持連線並立即偵測任何下降。使用 HTTP Keep-Alive 時，會重複使用基礎通訊端上建置的 Web 連線。boto3 一律會啟用 HTTP Keep-Alive。

閒置連線可以保持運作的時間有限制。如果您有閒置連線，但希望下一個請求使用已建立的連線，請考慮定期傳送請求 (每分鐘傳送一次)。

**重試的組態**

組態也會接受稱為**重試**的字典，您可以在其中指定所需的重試行為。當 SDK 收到錯誤且錯誤為暫時性類型時，會在 SDK 中重試。如果在內部重試錯誤 (重試最終產生成功的回應)，從呼叫程式碼的角度就不會看到錯誤，只是延遲稍微增加。以下是您可以指定的值：
+ **max\$1attempts** – 一個整數，表示對單一請求進行的重試嘗試次數上限。例如，若將此值設定為 2，初始請求後最多可重試請求兩次。若將此值設為 0，則在初始請求後不會嘗試任何重試。
+ **total\$1max\$1attempts** – 一個整數，代表對單一請求所嘗試的總數上限。這包括初始請求，因此值 1 表示不會重試任何請求。如果同時提供 `total_max_attempts` 和 `max_attempts`，則 `total_max_attempts` 具有優先權。`total_max_attempts` 優先於 `max_attempts`，因為它映射到 `AWS_MAX_ATTEMPTS` 環境變數和 `max_attempts` 組態檔案值。
+ **mode** – 一個字串，代表應該使用的重試模式 botocore 類型。有效的值如下：
  + **legacy** – 預設模式。第一次重試等待 50 毫秒，然後使用基本係數為 2 的指數退避。對於 DynamoDB，會執行最多總計 10 次嘗試 (除非以上述覆寫)。
**注意**  
使用指數退避時，最後一次嘗試將等待近 13 秒。
  + **標準** – 具名標準，因為它更符合其他 AWS SDKs。第一次重試將等待從 0 毫秒到 1,000 毫秒的隨機時間。如果需要再次重試，會挑選從 0 毫秒到 1,000 毫秒的另一個隨機時間，並乘以 2。如果需要額外的重試，會執行相同的隨機挑選乘以 4，依此類推。每次等待上限為 20 秒。相較於 `legacy` 模式，此模式在偵測到更多失敗條件時會執行重試。對於 DynamoDB，會執行最多總共 3 次嘗試 (除非以上述覆寫)。
  + **adaptive** - 實驗性重試模式，包含標準模式的所有功能，但新增了自動用戶端限流。透過自適應速率限制，SDKs可以降低傳送請求的速率，以更好地適應 AWS 服務的容量。這是一種暫時性模式，其行為可能會變更。

您可以在[重試指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html)中找到這些重試模式的擴展定義，也可以在 [SDK 參考的重試行為主題](https://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html)中找到。

以下是明確使用 `legacy` 重試政策的範例，總共最多 3 個請求 (2 次重試)：

```
import boto3
from botocore.config import Config

my_config = Config(
   connect_timeout = 1.0,
   read_timeout = 1.0,
   retries = {
     'mode': 'legacy',
     'total_max_attempts': 3
   }
)
dynamodb = boto3.resource('dynamodb', config=my_config)
```

由於 DynamoDB 是高度可用、低延遲的系統，因此建議您可以比內建重試政策所允許的程度，加快重試速度。您可以實作自己的重試政策，方法是將最大嘗試次數設定為 0，自行擷取例外狀況，並從自己的程式碼適當重試，而不是依賴 boto3 進行隱含重試。

如果您管理自己的重試政策，您會想要區分限流和錯誤：
+ **限流** (由 `ProvisionedThroughputExceededException` 或 `ThrottlingException` 表示) 表示運作狀態良好的服務，通知您已超過 DynamoDB 資料表或分割區的讀取或寫入容量。每經過一毫秒，就能獲得更多的讀取或寫入容量，因此您可以快速重試 (例如每 50 毫秒) 以嘗試存取該新發布的容量。使用限流時，您不需要特別使用指數退避，因為傳回限流對 DynamoDB 很輕量，而且不會向您收取每次請求的費用。指數退避會將較長的延遲指派給已等待最長時間的用戶端執行緒，以統計方式將 p50 和 p99 向外延伸。
+ **錯誤** (由 `InternalServerError` 或 `ServiceUnavailable` 等表示) 表示服務有暫時性問題。可能涉及整個資料表，也可能只是您正在讀取或寫入的分割區。發生錯誤時，您可以在重試之前暫停更長的時間 (例如 250 毫秒或 500 毫秒)，並使用抖動來交錯重試。

**集區連線上限的組態**

最後，組態可讓您控制連線集區大小：
+ **max\$1pool\$1connections (整數)** – 在連線集區中保留的最大連線數。如果未指定任何值，則會使用預設值 10。

此選項控制要保留在集區中以供重複使用的 HTTP 連線數目上限。每個工作階段會保留不同的集區。如果您預期針對同一工作階段建置的用戶端或資源有 10 個以上的執行緒，您應該考慮將此值提高，讓執行緒不必等待其他正在使用集區連線的執行緒。

```
import boto3
from botocore.config import Config

my_config = Config(
   max_pool_connections = 20
)

# Setup a single session holding up to 20 pooled connections
session = boto3.Session(my_config)

# Create up to 20 resources against that session for handing to threads
# Notice the single-threaded access to the Session and each Resource
resource1 = session.resource('dynamodb')
resource2 = session.resource('dynamodb')
# etc
```

## 錯誤處理
<a name="programming-with-python-error-handling"></a>

AWS Boto3 中並未靜態定義所有 服務例外狀況。這是因為 AWS 服務的錯誤和例外狀況差異很大，可能會有所變更。Boto3 將所有服務例外狀況包裝為 `ClientError`，並以結構化 JSON 公開詳細資訊。例如，錯誤回應的結構如下所示：

```
{
    'Error': {
        'Code': 'SomeServiceException',
        'Message': 'Details/context around the exception or error'
    },
    'ResponseMetadata': {
        'RequestId': '1234567890ABCDEF',
        'HostId': 'host ID data will appear here as a hash',
        'HTTPStatusCode': 400,
        'HTTPHeaders': {'header metadata key/values will appear here'},
        'RetryAttempts': 0
    }
}
```

下列程式碼會擷取任何 `ClientError` 例外狀況，並查看 `Error` 內的 `Code` 字串值，以決定要採取的動作：

```
import botocore
import boto3

dynamodb = boto3.client('dynamodb')

try:
    response = dynamodb.put_item(...)

except botocore.exceptions.ClientError as err:
    print('Error Code: {}'.format(err.response['Error']['Code']))
    print('Error Message: {}'.format(err.response['Error']['Message']))
    print('Http Code: {}'.format(err.response['ResponseMetadata']['HTTPStatusCode']))
    print('Request ID: {}'.format(err.response['ResponseMetadata']['RequestId']))

    if err.response['Error']['Code'] in ('ProvisionedThroughputExceededException', 'ThrottlingException'):
        print("Received a throttle")
    elif err.response['Error']['Code'] == 'InternalServerError':
        print("Received a server error")
    else:
        raise err
```

部分 (但並非全部) 例外狀況代碼已具體化為最高階類別。您可以選擇直接處理這些例外狀況。使用用戶端介面時，這些例外狀況會在您的用戶端上動態填入，而您使用用戶端執行個體來擷取這些例外狀況，如下所示：

```
except ddb_client.exceptions.ProvisionedThroughputExceededException:
```

使用資源介面時，您必須使用 `.meta.client` 從資源周遊到基礎用戶端來存取例外狀況，如下所示：

```
except ddb_resource.meta.client.exceptions.ProvisionedThroughputExceededException:
```

若要檢閱具體化例外狀況類型的清單，您可以動態產生清單：

```
ddb = boto3.client("dynamodb")
print([e for e in dir(ddb.exceptions) if e.endswith('Exception') or e.endswith('Error')])
```

使用條件表達式執行寫入操作時，您可以請求假如表達式失敗，應在錯誤回應中傳回項目的值。

```
try:
    response = table.put_item(
        Item=item,
        ConditionExpression='attribute_not_exists(pk)',
        ReturnValuesOnConditionCheckFailure='ALL_OLD'
    )
except table.meta.client.exceptions.ConditionalCheckFailedException as e:
    print('Item already exists:', e.response['Item'])
```

如需進一步了解錯誤處理和例外狀況：
+ [有關錯誤處理的 boto3 指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/error-handling.html)提供了有關錯誤處理技巧的詳細資訊。
+ [程式設計錯誤的 DynamoDB 開發人員指南區段](Programming.Errors.md)列出了您可能會遇到的錯誤。
+ [API 參考中的常見錯誤區段](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html)。
+ 每個 API 操作的文件會列出呼叫可能產生的錯誤 (例如 [BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html))。

## 日誌
<a name="programming-with-python-logging"></a>

boto3 程式庫與 Python 的內建記錄模組整合，以追蹤工作階段期間發生的情況。若要控制記錄層級，您可以設定記錄模組：

```
import logging

logging.basicConfig(level=logging.INFO)
```

這樣會設定根記錄器來記錄 `INFO` 和更高層級的訊息。系統會忽略嚴重程度低於層級的記錄訊息。記錄層級包括 `DEBUG`、`INFO`、`WARNING`、`ERROR` 和 `CRITICAL`。預設值為 `WARNING`。

boto3 中的記錄器是階層式的。程式庫使用幾個不同的記錄器，每個記錄器對應至程式庫的不同部分。您可以個別控制每個記錄器的行為：
+ **boto3**：boto3 模組的主要記錄器。
+ **botocore**：botocore 套件的主要記錄器。
+ **botocore.auth**：用於記錄請求的 AWS 簽章建立。
+ **botocore.credentials**：用於記錄憑證擷取和重新整理的程序。
+ **botocore.endpoint**：用於在透過網路傳送請求之前記錄建立請求。
+ **botocore.hooks**：用於記錄程式庫中觸發的事件。
+ **botocore.loaders**：用於載入部分 AWS 服務模型時的記錄。
+ **botocore.parsers**：用於在剖析之前記錄 AWS 服務回應。
+ **botocore.retryhandler**：用於記錄 AWS 服務請求重試的處理 （舊版）。
+ **botocore.retries.standard**：用於記錄 AWS 服務請求重試的處理 （標準或適應模式）。
+ **botocore.utils**：用於記錄程式庫中的其他活動。
+ **botocore.waiter**：用於記錄等待程式的功能，在達到特定狀態之前輪詢 AWS 服務。

其他程式庫也會記錄。在內部，boto3 會使用第三方 urllib3 來處理 HTTP 連線。當延遲很重要時，您可以監看其日誌，以確保您的集區在 urllib3 建立新連線或關閉閒置連線時獲得充分利用。
+ **urllib3.connectionpool：**用於記錄連線集區處理事件。

下列程式碼片段將大部分記錄設定為 `INFO`，`DEBUG` 記錄則用於端點和連線集區活動：

```
import logging

logging.getLogger('boto3').setLevel(logging.INFO)
logging.getLogger('botocore').setLevel(logging.INFO)
logging.getLogger('botocore.endpoint').setLevel(logging.DEBUG)
logging.getLogger('urllib3.connectionpool').setLevel(logging.DEBUG)
```

## 事件勾點
<a name="programming-with-python-event-hooks"></a>

Botocore 在執行的各個階段期間發出事件。您可以為這些事件註冊處理常式，以便每次發出事件時，都會呼叫您的處理常式。這可讓您擴充 botocore 的行為，而不必修改其內部。

例如，假設您希望每次在應用程式中對任何 DynamoDB 資料表呼叫 `PutItem` 操作時加以記錄。您可以在 `'provide-client-params.dynamodb.PutItem'` 事件上註冊，以便每次在關聯的工作階段上調用 `PutItem` 操作時擷取和記錄。範例如下：

```
import boto3
import botocore
import logging

def log_put_params(params, **kwargs):
    if 'TableName' in params and 'Item' in params:
        logging.info(f"PutItem on table {params['TableName']}: {params['Item']}")

logging.basicConfig(level=logging.INFO)

session = boto3.Session()
event_system = session.events

# Register our interest in hooking in when the parameters are provided to PutItem
event_system.register('provide-client-params.dynamodb.PutItem', log_put_params)

# Now, every time you use this session to put an item in DynamoDB,
# it will log the table name and item data.
dynamodb = session.resource('dynamodb')
table = dynamodb.Table('YourTableName')
table.put_item(
    Item={
        'pk': '123',
        'sk': 'cart#123',
        'item_data': 'YourItemData',
        # ... more attributes ...
    }
)
```

在處理常式中，您甚至可以透過程式設計方式操作參數來變更行為：

```
params['TableName'] = "NewTableName"
```

如需事件的詳細資訊，請參閱[事件的 botocore 文件](https://botocore.amazonaws.com/v1/documentation/api/latest/topics/events.html)和[事件的 boto3 文件](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/events.html)。

## 分頁和分頁程式
<a name="programming-with-python-pagination"></a>

有些請求，例如查詢和掃描，會限制單一請求上傳回的資料大小，並要求您重複提出請求以提取後續分頁。

您可以使用 `limit` 參數控制每個分頁要讀取的項目數量上限。例如，如果您想要最後 10 個項目，您可以使用 `limit` 僅擷取最後 10 個項目。請注意，限制是指套用任何篩選之前，應該從資料表讀取多少。篩選後無法指定您想要的確切 10 個項目；您只能控制預先篩選的計數，並在實際擷取 10 個項目時在用戶端檢查。無論限制為何，每個回應的大小上限一律為 1 MB。

如果回應包含 `LastEvaluatedKey`，則表示回應因為達到計數或大小限制而結束。索引鍵是針對回應評估的最後一個鍵。您可以擷取此 `LastEvaluatedKey` 並將其傳遞至後續呼叫做為 `ExclusiveStartKey`，以讀取該起點的下一個區塊。沒有 `LastEvaluatedKey` 傳回該項目時，表示沒有更多符合查詢或掃描的項目。

以下是一個簡單的範例 (使用資源介面，但用戶端介面具有相同的模式)，每個頁面和迴圈最多讀取 100 個項目，直到所有項目都已讀取為止。

```
import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('YourTableName')

query_params = {
    'KeyConditionExpression': Key('pk').eq('123') & Key('sk').gt(1000),
    'Limit': 100
}

while True:
    response = table.query(**query_params)

    # Process the items however you like
    for item in response['Items']:
        print(item)

    # No LastEvaluatedKey means no more items to retrieve
    if 'LastEvaluatedKey' not in response:
        break

    # If there are possibly more items, update the start key for the next page
    query_params['ExclusiveStartKey'] = response['LastEvaluatedKey']
```

為了方便起見，boto3 可以使用分頁程式為您執行此操作。不過，它僅適用於用戶端介面。以下是改寫為使用分頁程式的程式碼：

```
import boto3

dynamodb = boto3.client('dynamodb')

paginator = dynamodb.get_paginator('query')

query_params = {
    'TableName': 'YourTableName',
    'KeyConditionExpression': 'pk = :pk_val AND sk > :sk_val',
    'ExpressionAttributeValues': {
        ':pk_val': {'S': '123'},
        ':sk_val': {'N': '1000'},
    },
    'Limit': 100
}

page_iterator = paginator.paginate(**query_params)

for page in page_iterator:
    # Process the items however you like
    for item in page['Items']:
        print(item)
```

如需詳細資訊，請參閱[分頁程式指南](https://botocore.amazonaws.com/v1/documentation/api/latest/topics/events.html)和 [DynamoDB.Paginator.Query 的 API 參考](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/paginator/Query.html)。

**注意**  
分頁程式也有自己的組態設定，名為 `MaxItems`、`StartingToken` 和 `PageSize`。若要使用 DynamoDB 進行分頁，您應該忽略這些設定。

## 等待程式
<a name="programming-with-python-waiters"></a>

等待程式提供在繼續之前等待某些項目完成的能力。目前，僅支援等待建立或刪除資料表。在背景中，等待程式操作會每 20 秒為您檢查一次，最多 25 次。您可以自行執行此操作，但在撰寫自動化時，使用等待程式是一種簡練的方式。

此程式碼說明如何等待特定資料表建立完成：

```
# Create a table, wait until it exists, and print its ARN
response = client.create_table(...)
waiter = client.get_waiter('table_exists')
waiter.wait(TableName='YourTableName')
print('Table created:', response['TableDescription']['TableArn']
```

如需詳細資訊，請參閱[等待程式指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html#waiters)和[等待程式參考](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#waiters)。