跳至內容

如何同時變更多個 Amazon DynamoDB 表的容量模式?

9 分的閱讀內容
0

我想要同時變更多個 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 表的容量模式變更為佈建模式,請完成下列步驟:

  1. 開啟文字編輯器,輸入以下程式碼來建立一個新的 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_VALUEWRITE_CAPACITY_VALUE 替換為您的讀取和寫入容量值。

  2. 使用名稱 switch-all-tables-with-gsi-to-provisioned.sh 儲存該檔案。

  3. 若要使檔案可執行,請開啟終端並執行以下命令:

    chmod +x switch-all-tables-with-gsi-to-provisioned.sh
  4. 若要在終端中執行 shell 指令碼,請執行以下命令:

    ./switch-all-tables-with-gsi-to-provisioned.sh

隨需模式

若要使用 AWS CLI 將多個 DynamoDB 表的容量模式變更為隨需模式,請完成下列步驟:

  1. 開啟文字編輯器,然後輸入以下程式碼來建立新的 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

  2. 若要使檔案可執行,請開啟終端並執行命令:

    chmod +x switch-all-tables-with-gsi-to-ondemand.sh
  3. 若要在終端中執行 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 表的容量模式變更為佈建模式,請完成下列步驟:

  1. 開啟文字編輯器並輸入以下程式碼,以建立新的 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: '*'
  2. 使用名稱 switch-to-provisioned.yaml 儲存該檔案。

  3. 執行以下 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_VALUEWCU_VALUE 替換為您的 RCU 和 WCU 值,並將 REGION 替換為您的區域,例如 us-east-1

隨需模式

若要使用 CloudFormation 將多個 DynamoDB 表的容量模式變更為隨需模式,請完成下列步驟:

  1. 開啟文字編輯器並輸入以下程式碼,以建立新的 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: '*'
  2. 儲存該檔案,並將其命名為 switch-to-ondemand.yaml

  3. 執行以下 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 表的容量模式變更為佈建模式,請完成下列步驟:

  1. 開啟 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_VALUEWCU_VALUE 替換為您的 RCU 和 WCU 值,並將 REGION 替換為您的區域,例如 us-east-1

  2. 使用 switch_to_provisioned.py 名稱儲存該檔案。

  3. 開啟終端並執行以下命令,以執行 Python 指令碼:

    python switch_to_provisioned.py

隨需模式

若要使用 Python 指令碼將特定區域內所有 DynamoDB 表的容量模式變更為隨需模式,請完成下列步驟:

  1. 開啟 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 替換為您的區域。

  2. 使用名稱 switch_to_ondemand.py 儲存該檔案。

  3. 開啟終端並執行以下命令,以執行 Python 指令碼:

    python switch_to_ondemand.py

相關資訊

DynamoDB 輸送容量

AWS 官方已更新 9 個月前