使用 Python 和 Boto3 為 Amazon DynamoDB 編寫程式
本指南為想要透過 Python 使用 Amazon DynamoDB 的程式設計人員提供指導。了解不同的抽象層、組態管理、錯誤處理、控制重試政策、管理保持連線等。
主題
關於 Boto
您可以使用適用於 Python 的官方 AWS SDK 從 Python 存取 DynamoDB,通常稱為 Boto3。Boto (發音為 boh-toh) 的名稱來自亞馬遜河原生的淡水海豚。Boto3 程式庫是程式庫的第三個主要版本,於 2015 年首次發行。Boto3 程式庫相當大,因為它支援所有 AWS 服務,不只是 DynamoDB。此指導僅針對 Boto3 中與 DynamoDB 相關的部分。
Boto 由 AWS 維護並發布為在 GitHub 上託管的開放原始碼專案,分為兩個套件:Botocore
-
Botocore 提供低階功能。在 Botocore 中,您會找到用戶端、工作階段、憑證、組態和例外類別。
-
Boto3 建置在 Botocore 之上,提供更高階、更具 Python 風格的介面。具體而言,它公開 DynamoDB 資料表做為資源,並提供比服務導向的低階用戶端介面更簡單、更優雅的介面。
由於這些專案託管在 GitHub 上,因此您可以檢視原始程式碼、追蹤未解決的問題,或提交您自己的問題。
使用 Boto 文件
使用下列資源開始使用 Boto 文件:
-
從 Quickstart 區段
開始,該區段提供安裝套件的堅實起點。如需安裝 Boto3 的說明,請前往該處 (Boto3 通常可在 AWS Lambda 等 AWS 服務內自動取得)。 -
之後,請專注於文件的 DynamoDB 指南
。它說明如何執行基本的 DynamoDB 活動:建立和刪除資料表、操作項目,以及執行批次操作、查詢和掃描。其範例使用資源介面。當您看到 boto3.resource('dynamodb'),表示您正在使用較高階的資源介面。 -
在本指南之後,您可以檢閱 DynamoDB 參考
。此登陸頁面提供您可使用的類別和方法的完整清單。在頂端,您會看到 DynamoDB.Client類別,提供所有控制平面和資料平面操作的低階存取。在底部則可查看DynamoDB.ServiceResource類別,這是更高階且具有 Python 風格的介面。您可以使用它來建立資料表、跨資料表執行批次操作,或取得資料表特定動作的DynamoDB.ServiceResource.Table執行個體。
了解用戶端和資源抽象層
您將使用的兩個介面是用戶端介面和資源介面。
-
低階用戶端介面提供對基礎服務 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
注意
考慮使用用戶端或資源介面進行開發時,請注意,根據以下資源文件
使用資料表資源 batch_writer
batch_writer 是一種便利的方法,僅適用於較高階的資料表資源。DynamoDB 支援批次寫入操作,在一個網路請求中最多允許 25 個放置或刪除操作。像這樣的批次處理可最大限度減少網路往返,從而提高效率。
透過低階用戶端程式庫,您可以使用 client.batch_write_item() 操作來執行批次。您必須手動將工作分成 25 個批次。在每次操作之後,您也必須請求獲得未處理項目的清單 (有些寫入操作可能會成功,有些則可能會失敗)。然後,您必須將這些未處理的項目再次傳遞至稍後的 batch_write_item() 操作。有大量的樣板程式碼。
Table.batch_writer
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)
探索用戶端和資源層的其他程式碼範例
您也可以參考下列程式碼範例儲存庫,以探索在使用用戶端和資源的情況下,各種函數的使用方式:
了解用戶端和資源物件如何與工作階段和執行緒互動
資源物件不具備執行緒安全性,不應跨執行緒或程序共用。如需詳細資訊,請參閱資源指南
相反地,用戶端物件通常具備執行緒安全性,但特定進階功能除外。如需更多詳細資訊,請參閱用戶端指南
工作階段物件不具備執行緒安全性。因此,每次在多執行緒環境中建立用戶端或資源時,應先建立新的工作階段,然後從工作階段建立用戶端或資源。如需詳細資訊,請參閱工作階段指南
當您呼叫 boto3.resource() 時,您會隱含地使用預設工作階段。這有助於撰寫單執行緒程式碼。撰寫多執行緒程式碼時,您會想要先為每個執行緒建構新的工作階段,然後從該工作階段擷取資源:
# Explicitly create a new Session for this thread session = boto3.Session() dynamodb = session.resource('dynamodb')
自訂 Config 物件
建構用戶端或資源物件時,您可以傳遞選用的已命名參數來自訂行為。名為 config 的參數會解鎖各種功能。這是 botocore.client.Config 的執行個體,而 Config 參考文件
注意
您可以修改在工作階段層級、AWS 組態檔案內或作為環境變數的許多行為設定。
逾時的組態
自訂組態的一個用途是調整聯網行為:
-
connect_timeout (浮點數或整數) – 嘗試建立連線時擲出逾時例外狀況的秒數。預設值為 60 秒。
-
read_timeout (浮點數或整數) – 嘗試從連線讀取時擲出逾時例外狀況前的秒數。預設值為 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 請求設定
保持連線的組態
如果您使用的是 botocore 1.27.84 或更新版本,您也可以控制 TCP Keep-Alive:
-
tcp_keepalive (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_attempts – 一個整數,表示對單一請求進行的重試嘗試次數上限。例如,若將此值設定為 2,初始請求後最多可重試請求兩次。若將此值設為 0,則在初始請求後不會嘗試任何重試。
-
total_max_attempts – 一個整數,代表對單一請求所嘗試的總數上限。這包括初始請求,因此值 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 秒。
-
standard – 命名為 standard (標準),因為與其他 AWS SDK 更一致。第一次重試將等待從 0 毫秒到 1,000 毫秒的隨機時間。如果需要再次重試,會挑選從 0 毫秒到 1,000 毫秒的另一個隨機時間,並乘以 2。如果需要額外的重試,會執行相同的隨機挑選乘以 4,依此類推。每次等待上限為 20 秒。相較於
legacy模式,此模式在偵測到更多失敗條件時會執行重試。對於 DynamoDB,會執行最多總共 3 次嘗試 (除非以上述覆寫)。 -
adaptive - 實驗性重試模式,包含標準模式的所有功能,但新增了自動用戶端限流。透過自適應速率限制,SDK 可以減慢傳送請求的速率,以更好地適應 AWS 服務的容量。這是一種暫時性模式,其行為可能會變更。
-
您可以在重試指南
以下是明確使用 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_pool_connections (整數) – 在連線集區中保留的最大連線數。如果未指定任何值,則會使用預設值 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
錯誤處理
Boto3 中並未靜態定義所有 AWS 服務例外狀況。這是因為 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 指南
提供了有關錯誤處理技巧的詳細資訊。 -
程式設計錯誤的 DynamoDB 開發人員指南區段列出了您可能會遇到的錯誤。
-
每個 API 操作的文件會列出呼叫可能產生的錯誤 (例如 BatchWriteItem)。
日誌
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)
事件勾點
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 文件
分頁和分頁程式
有些請求,例如查詢和掃描,會限制單一請求上傳回的資料大小,並要求您重複提出請求以提取後續分頁。
您可以使用 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)
如需詳細資訊,請參閱分頁程式指南
注意
分頁程式也有自己的組態設定,名為 MaxItems、StartingToken 和 PageSize。若要使用 DynamoDB 進行分頁,您應該忽略這些設定。
等待程式
等待程式提供在繼續之前等待某些項目完成的能力。目前,僅支援等待建立或刪除資料表。在背景中,等待程式操作會每 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']