如何同時變更多個 Amazon DynamoDB 表的容量模式?
我想要同時變更多個 Amazon DynamoDB 表的容量模式。
簡短描述
當變更多個 DynamoDB 表的容量模式時,必須指定佈建容量模式或隨需容量模式。在變更容量模式之前,請參閱在 DynamoDB 中切換容量模式時的注意事項。
若要同時變更多個 DynamoDB 表的容量模式,請使用下列其中一種方法:
- AWS Command Line Interface (AWS CLI)
- AWS CloudFormation
- Python
解決方法
最佳做法
在變更多個 DynamoDB 表的容量模式時,請遵循以下最佳做法:
- 在啟動變更之前,請設定適當的 AWS CLI 憑證。
- 請確認您擁有適當的 AWS Identity and Access Management (IAM) 權限。
- 先將您的變更部署到非生產環境。
- 等待幾分鐘讓每張資料表完成切換。
- 每張資料表每 24 小時僅可切換一次容量模式。
- 分析使用模式以選取適當的容量模式,並根據 AWS 區域要求進行調整。
- 監控切換後的成本,以確保您擁有適當的佈建容量。
AWS CLI
**注意:**如果您在執行 AWS Command Line Interface (AWS CLI) 命令時收到錯誤訊息,請參閱對 AWS CLI 錯誤進行疑難排解。此外,請確定您使用的是最新的 AWS CLI 版本。
佈建模式
若要使用 AWS CLI 將多個 DynamoDB 表的容量模式變更為佈建模式,請完成下列步驟:
-
開啟文字編輯器,輸入以下程式碼來建立一個新的 shell 指令碼:
#!/bin/bash # Set the AWS region AWS_REGION=[REGION] # Change this to your desired region # OPTION1: List of table names you want to switch TABLES=("table1" "table2" "table3") # OPTION2: Get all table names in the account TABLES=$(aws dynamodb list-tables —region $AWS_REGION —query 'TableNames[]' —output text) # Default provisioned capacity units READ_CAPACITY=READ_CAPACITY_VALUE WRITE_CAPACITY=WRITE_CAPACITY_VALUE echo "Using AWS Region: $AWS_REGION" for TABLE_NAME in $TABLES do # Check current billing mode CURRENT_MODE=$(aws dynamodb describe-table —region $AWS_REGION —table-name $TABLE_NAME —query 'Table.BillingModeSummary.BillingMode' —output text) if [ "$CURRENT_MODE" = "PAY_PER_REQUEST" ]; then echo "Processing table: $TABLE_NAME" # Get GSI configurations GSI_CONFIG="" GSI_LIST=$(aws dynamodb describe-table —region $AWS_REGION —table-name $TABLE_NAME —query 'Table.GlobalSecondaryIndexes[*].IndexName' —output text) if [ ! -z "$GSI_LIST" ]; then echo "Found GSIs: $GSI_LIST" # Build GSI provisioned throughput configuration GSI_CONFIG="—global-secondary-index-updates“ for GSI_NAME in $GSI_LIST do if [ -z "$FIRST_GSI" ]; then GSI_CONFIG="$GSI_CONFIG [{\"Update\":{\"IndexName\":\"$GSI_NAME\",\"ProvisionedThroughput\":{\"ReadCapacityUnits\":$READ_CAPACITY,\"WriteCapacityUnits\":$WRITE_CAPACITY}}}" FIRST_GSI="false" else GSI_CONFIG="$GSI_CONFIG,{\"Update\":{\"IndexName\":\"$GSI_NAME\",\"ProvisionedThroughput\":{\"ReadCapacityUnits\":$READ_CAPACITY,\"WriteCapacityUnits\":$WRITE_CAPACITY}}}" fi done GSI_CONFIG="$GSI_CONFIG]" fi # Update table and GSIs if [ ! -z "$GSI_CONFIG" ]; then echo "Updating table and GSIs..." aws dynamodb update-table \ --region $AWS_REGION \ --table-name $TABLE_NAME \ --billing-mode PROVISIONED \ --provisioned-throughput ReadCapacityUnits=$READ_CAPACITY,WriteCapacityUnits=$WRITE_CAPACITY \ $GSI_CONFIG else echo "Updating table (no GSIs)..." aws dynamodb update-table \ --region $AWS_REGION \ --table-name $TABLE_NAME \ --billing-mode PROVISIONED \ --provisioned-throughput ReadCapacityUnits=$READ_CAPACITY,WriteCapacityUnits=$WRITE_CAPACITY fi echo "Request submitted for $TABLE_NAME" else echo "Skipping $TABLE_NAME - already in PROVISIONED mode" fi # Reset GSI tracking for next table FIRST_GSI="" echo "----------------------------------------" done若要將所有 DynamoDB 表的容量模式變更為隨需模式,請移除下列程式碼片段:
#OPTION1: List of table names you want to switch TABLES=("table1" "table2" "table3")若要將特定 DynamoDB 表的容量模式變更為隨需模式,請將 “table1” “table2” “table3” 替換為您的資料表名稱。然後,移除以下程式碼片段:
#OPTION2: Get all table names in the account TABLES=$(aws dynamodb list-tables —query 'TableNames[]' —output text)**注意:**將 READ_CAPACITY_VALUE 和 WRITE_CAPACITY_VALUE 替換為您的讀取和寫入容量值。
-
使用名稱 switch-all-tables-with-gsi-to-provisioned.sh 儲存該檔案。
-
若要使檔案可執行,請開啟終端並執行以下命令:
chmod +x switch-all-tables-with-gsi-to-provisioned.sh -
若要在終端中執行 shell 指令碼,請執行以下命令:
./switch-all-tables-with-gsi-to-provisioned.sh
隨需模式
若要使用 AWS CLI 將多個 DynamoDB 表的容量模式變更為隨需模式,請完成下列步驟:
-
開啟文字編輯器,然後輸入以下程式碼來建立新的 shell 指令碼:
#!/bin/bash # Set the AWS region AWS_REGION=[REGION] # Change this to your desired region # OPTION1: List of table names you want to switch TABLES=("table1" "table2" "table3") # OPTION2: Get all table names in the account #TABLES=$(aws dynamodb list-tables --region $AWS_REGION --query 'TableNames[]' --output text) for TABLE_NAME in $TABLES do # Check current billing mode CURRENT_MODE=$(aws dynamodb describe-table --region $AWS_REGION --table-name $TABLE_NAME --query 'Table.BillingModeSummary.BillingMode' --output text) if [ "$CURRENT_MODE" = "PROVISIONED" ]; then echo "Processing table: $TABLE_NAME" # Check if table has any GSIs GSI_LIST=$(aws dynamodb describe-table --region $AWS_REGION --table-name $TABLE_NAME --query 'Table.GlobalSecondaryIndexes[*].IndexName' --output text) if [ ! -z "$GSI_LIST" ]; then echo "Table has GSIs: $GSI_LIST" echo "Note: GSIs will automatically switch to On-Demand with the table" fi # Update table to On-Demand echo "Switching $TABLE_NAME to PAY_PER_REQUEST mode..." aws dynamodb update-table \ --region $AWS_REGION \ --table-name $TABLE_NAME \ --billing-mode PAY_PER_REQUEST echo "Request submitted for $TABLE_NAME" else echo "Skipping $TABLE_NAME - already in PAY_PER_REQUEST mode" fi echo "----------------------------------------" done若要將所有 DynamoDB 表的容量模式變更為隨需模式,請移除下列程式碼片段:
#OPTION1: List of table names you want to switch TABLES=("table1" "table2" "table3")若要將特定 DynamoDB 表的容量模式變更為隨需模式,請將 “table1” “table2” “table3” 替換為您的資料表名稱。然後,移除以下程式碼片段:
`#OPTION2: Get all table names in the account` TABLES=$(aws dynamodb list-tables —region $AWS\_REGION —query 'TableNames\[\]' —output text)**注意:**將 REGION 替換為您的 Region。使用區域代碼,例如 us-east-1。
-
若要使檔案可執行,請開啟終端並執行命令:
chmod +x switch-all-tables-with-gsi-to-ondemand.sh -
若要在終端中執行 shell 指令碼,請執行命令:
./switch-all-tables-with-gsi-to-ondemand.sh
CloudFormation
請使用下列最佳做法:
- 將 AWS Lambda 範本設定為使用 Python 3.9 執行時期。
- 監控 Amazon CloudWatch Logs 以追蹤 Lambda 函式的進度。
**注意:**開始之前,請設定您的 AWS 憑證。執行以下設定 AWS CLI 命令:
aws configure
佈建模式
若要使用 CloudFormation 將多個 DynamoDB 表的容量模式變更為佈建模式,請完成下列步驟:
-
開啟文字編輯器並輸入以下程式碼,以建立新的 YAML 檔案:
AWSTemplateFormatVersion: '2010-09-09' Description: 'Switch specific DynamoDB tables from On-Demand to Provisioned capacity mode' Parameters: ReadCapacityUnits: Type: Number Default: 5 Description: Read Capacity Units for tables and GSIs WriteCapacityUnits: Type: Number Default: 5 Description: Write Capacity Units for tables and GSIs TableNames: Type: CommaDelimitedList Description: Comma-separated list of DynamoDB table names to update Resources: DynamoDBTableUpdates: Type: Custom::DynamoDBTableUpdates Properties: ServiceToken: !GetAtt UpdateTablesFunction.Arn ReadCapacityUnits: !Ref ReadCapacityUnits WriteCapacityUnits: !Ref WriteCapacityUnits TableNames: !Ref TableNames UpdateTablesFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.9 Handler: index.handler Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: | import boto3 import cfnresponse def handler(event, context): try: if event['RequestType'] in ['Create', 'Update']: dynamodb = boto3.client('dynamodb') # Get parameters read_capacity = event['ResourceProperties']['ReadCapacityUnits'] write_capacity = event['ResourceProperties']['WriteCapacityUnits'] table_names = event['ResourceProperties']['TableNames'] for table_name in table_names: try: # Get table details table = dynamodb.describe_table(TableName=table_name)['Table'] current_mode = table.get('BillingModeSummary', {}).get('BillingMode', '') if current_mode == 'PAY_PER_REQUEST': # Prepare GSI updates if any gsi_updates = [] if 'GlobalSecondaryIndexes' in table: for gsi in table['GlobalSecondaryIndexes']: gsi_updates.append({ 'Update': { 'IndexName': gsi['IndexName'], 'ProvisionedThroughput': { 'ReadCapacityUnits': int(read_capacity), 'WriteCapacityUnits': int(write_capacity) } } }) # Update table update_params = { 'TableName': table_name, 'BillingMode': 'PROVISIONED', 'ProvisionedThroughput': { 'ReadCapacityUnits': int(read_capacity), 'WriteCapacityUnits': int(write_capacity) } } if gsi_updates: update_params['GlobalSecondaryIndexUpdates'] = gsi_updates dynamodb.update_table(**update_params) print(f"Switching {table_name} to PROVISIONED mode") else: print(f"Table {table_name} is not in PAY_PER_REQUEST mode. Skipping.") except Exception as e: print(f"Error processing table {table_name}: {str(e)}") continue cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) else: cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) except Exception as e: print(f"Error: {str(e)}") cfnresponse.send(event, context, cfnresponse.FAILED, {}) LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: DynamoDBAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - dynamodb:DescribeTable - dynamodb:UpdateTable Resource: '*' -
使用名稱 switch-to-provisioned.yaml 儲存該檔案。
-
執行以下 create-stack AWS CLI 命令:
# For Provisioned mode aws cloudformation create-stack \ --stack-name switch-to-provisioned \ --template-body file://switch-to-provisioned.yaml \ --capabilities CAPABILITY_IAM \ --region [REGION] \ --parameters ParameterKey=TableNames,ParameterValue="Table1,Table2,Table3" \ ParameterKey=ReadCapacityUnits,ParameterValue=[RCU_VALUE] \ ParameterKey=WriteCapacityUnits,ParameterValue=[WCU_VALUE]**注意:**將 “Table1,Table2,Table3” 替換為您的資料表名稱,將 RCU_VALUE 和 WCU_VALUE 替換為您的 RCU 和 WCU 值,並將 REGION 替換為您的區域,例如 us-east-1。
隨需模式
若要使用 CloudFormation 將多個 DynamoDB 表的容量模式變更為隨需模式,請完成下列步驟:
-
開啟文字編輯器並輸入以下程式碼,以建立新的 YAML 檔案:
AWSTemplateFormatVersion: '2010-09-09' Description: 'Switch specific DynamoDB tables from Provisioned to On-Demand capacity mode' Parameters: TableNames: Type: CommaDelimitedList Description: Comma-separated list of DynamoDB table names to update Resources: DynamoDBTableUpdates: Type: Custom::DynamoDBTableUpdates Properties: ServiceToken: !GetAtt UpdateTablesFunction.Arn TableNames: !Ref TableNames UpdateTablesFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.9 Handler: index.handler Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: | import boto3 import cfnresponse def handler(event, context): try: if event['RequestType'] in ['Create', 'Update']: dynamodb = boto3.client('dynamodb') # Get table names from the event table_names = event['ResourceProperties']['TableNames'] for table_name in table_names: try: # Get table details table = dynamodb.describe_table(TableName=table_name)['Table'] current_mode = table.get('BillingModeSummary', {}).get('BillingMode', '') if current_mode == 'PROVISIONED': # Update table to On-Demand dynamodb.update_table( TableName=table_name, BillingMode='PAY_PER_REQUEST' ) print(f"Switching {table_name} to PAY_PER_REQUEST mode") else: print(f"Table {table_name} is not in PROVISIONED mode. Skipping.") except Exception as e: print(f"Error processing table {table_name}: {str(e)}") continue cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) else: cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) except Exception as e: print(f"Error: {str(e)}") cfnresponse.send(event, context, cfnresponse.FAILED, {}) LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: DynamoDBAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - dynamodb:DescribeTable - dynamodb:UpdateTable Resource: '*' -
儲存該檔案,並將其命名為 switch-to-ondemand.yaml。
-
執行以下 create-stack AWS CLI 命令:
# For On-Demand mode aws cloudformation create-stack \ --stack-name switch-to-ondemand \ --template-body file://switch-to-ondemand.yaml \ --capabilities CAPABILITY_IAM \ --region [REGION] \ --parameters ParameterKey=TableNames,ParameterValue="Table1,Table2,Table3"**注意:**將 “Table1,Table2,Table3” 替換為您的資料表名稱,並將 REGION 替換為您的區域。
Python
您可以使用 Amazon Elastic Compute Cloud (EC2) 執行個體、Lambda 或您自己的桌面來執行 Python 指令碼。在變更容量模式之前,請確認您已安裝 Python、pip 和 boto3。
在開始之前,請先設定您的 AWS 憑證。執行以下設定 AWS CLI 命令:
aws configure
佈建模式
若要使用 Python 指令碼將特定區域內所有 DynamoDB 表的容量模式變更為佈建模式,請完成下列步驟:
-
開啟 Python,輸入以下程式碼,建立新檔案:
import boto3 import time from botocore.exceptions import ClientError def switch_to_provisioned(read_capacity=5, write_capacity=5, region=None): """ Switch all DynamoDB tables from On-Demand to Provisioned capacity mode """ # Initialize DynamoDB client dynamodb = boto3.client('dynamodb', region_name=region) # Get all table names tables = [] paginator = dynamodb.get_paginator('list_tables') for page in paginator.paginate(): tables.extend(page['TableNames']) print(f"Found {len(tables)} tables") for table_name in tables: try: # Get table details response = dynamodb.describe_table(TableName=table_name) table = response['Table'] current_mode = table.get('BillingModeSummary', {}).get('BillingMode', '') if current_mode == 'PAY_PER_REQUEST': print(f"\nProcessing table: {table_name}") # Prepare GSI updates if any gsi_updates = [] if 'GlobalSecondaryIndexes' in table: print(f"Found GSIs for table {table_name}") for gsi in table['GlobalSecondaryIndexes']: gsi_updates.append({ 'Update': { 'IndexName': gsi['IndexName'], 'ProvisionedThroughput': { 'ReadCapacityUnits': read_capacity, 'WriteCapacityUnits': write_capacity } } }) # Prepare update parameters update_params = { 'TableName': table_name, 'BillingMode': 'PROVISIONED', 'ProvisionedThroughput': { 'ReadCapacityUnits': read_capacity, 'WriteCapacityUnits': write_capacity } } if gsi_updates: update_params['GlobalSecondaryIndexUpdates'] = gsi_updates # Update table print(f"Switching {table_name} to PROVISIONED mode...") dynamodb.update_table(**update_params) print(f"Update request submitted for {table_name}") else: print(f"\nSkipping {table_name} - already in PROVISIONED mode") except ClientError as e: if e.response['Error']['Code'] == 'LimitExceededException': print(f"\nError: Cannot update {table_name}. You can only switch between billing modes once per 24 hours.") else: print(f"\nError processing table {table_name}: {str(e)}") continue except Exception as e: print(f"\nUnexpected error processing table {table_name}: {str(e)}") continue # Small delay to avoid API throttling time.sleep(1) if __name__ == "__main__": # You can modify these values READ_CAPACITY = [RCU_VALUE] WRITE_CAPACITY = [WCU_VALUE] REGION = [REGION] # Change to your desired region switch_to_provisioned( read_capacity=READ_CAPACITY, write_capacity=WRITE_CAPACITY, region=REGION )**注意:**將 RCU_VALUE 和 WCU_VALUE 替換為您的 RCU 和 WCU 值,並將 REGION 替換為您的區域,例如 us-east-1。
-
使用 switch_to_provisioned.py 名稱儲存該檔案。
-
開啟終端並執行以下命令,以執行 Python 指令碼:
python switch_to_provisioned.py
隨需模式
若要使用 Python 指令碼將特定區域內所有 DynamoDB 表的容量模式變更為隨需模式,請完成下列步驟:
-
開啟 Python,輸入以下程式碼,建立新檔案:
import boto3 import time from botocore.exceptions import ClientError def switch_to_ondemand(region=None): """ Switch all DynamoDB tables from Provisioned to On-Demand capacity mode """ # Initialize DynamoDB client dynamodb = boto3.client('dynamodb', region_name=region) # Get all table names tables = [] paginator = dynamodb.get_paginator('list_tables') for page in paginator.paginate(): tables.extend(page['TableNames']) print(f"Found {len(tables)} tables") for table_name in tables: try: # Get table details response = dynamodb.describe_table(TableName=table_name) table = response['Table'] current_mode = table.get('BillingModeSummary', {}).get('BillingMode', '') if current_mode == 'PROVISIONED': print(f"\nProcessing table: {table_name}") # Check for GSIs if 'GlobalSecondaryIndexes' in table: print(f"Table {table_name} has GSIs - they will automatically switch to On-Demand") # Update table print(f"Switching {table_name} to PAY_PER_REQUEST mode...") dynamodb.update_table( TableName=table_name, BillingMode='PAY_PER_REQUEST' ) print(f"Update request submitted for {table_name}") else: print(f"\nSkipping {table_name} - already in PAY_PER_REQUEST mode") except ClientError as e: if e.response['Error']['Code'] == 'LimitExceededException': print(f"\nError: Cannot update {table_name}. You can only switch between billing modes once per 24 hours.") else: print(f"\nError processing table {table_name}: {str(e)}") continue except Exception as e: print(f"\nUnexpected error processing table {table_name}: {str(e)}") continue # Small delay to avoid API throttling time.sleep(1) def check_table_status(table_name, region=None): """ Check the current billing mode of a specific table """ dynamodb = boto3.client('dynamodb', region_name=region) try: response = dynamodb.describe_table(TableName=table_name) mode = response['Table'].get('BillingModeSummary', {}).get('BillingMode', 'Unknown') print(f"Table {table_name} is in {mode} mode") return mode except Exception as e: print(f"Error checking table {table_name}: {str(e)}") return None if __name__ == "__main__": REGION = [REGION] # Change to your desired region switch_to_ondemand(region=REGION)**注意:**將 REGION 替換為您的區域。
-
使用名稱 switch_to_ondemand.py 儲存該檔案。
-
開啟終端並執行以下命令,以執行 Python 指令碼:
python switch_to_ondemand.py
