如何同时更改多个 Amazon DynamoDB 表的容量模式?
我想同时更改多个 Amazon DynamoDB 表的容量模式。
简短描述
当您更改多个 DynamoDB 表的容量模式时,必须指定预置容量模式或按需容量模式。在更改容量模式之前,请参阅在 DynamoDB 中切换容量模式时的注意事项。
要同时更改多个 DynamoDB 表的容量模式,请使用以下方法之一:
- AWS 命令行界面 (AWS CLI)
- AWS CloudFormation
- Python
解决方法
最佳实践
当您更改多个 DynamoDB 表的容量模式时,请遵循以下最佳实践:
- 在启动更改之前,请配置适当的 AWS CLI 凭证。
- 确保您拥有适当的 AWS Identity and Access Management (IAM) 权限。
- 首先将您的更改部署到非生产环境。
- 为每个表预留数分钟以完成切换。
- 每个表每 24 小时仅可切换一次容量模式。
- 分析使用模式以选择适当的容量模式,并根据 AWS 区域要求进行调整。
- 在切换后监控成本,以确保您拥有适当的预置容量。
AWS CLI
**注意:**如果您在运行 AWS 命令行界面 (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 替换为您的区域。使用该区域的代码,例如 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 凭证。运行以下 configure 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 凭证。运行以下 configure 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
