

# Using the DynamoDB Well-Architected Lens to optimize your DynamoDB workload
<a name="bp-wal"></a>

This section describes the Amazon DynamoDB Well-Architected Lens, a collection of design principles and guidance for designing well-architected DynamoDB workloads.

# Optimizing costs on DynamoDB tables
<a name="bp-cost-optimization"></a>

This section covers best practices on how to optimize costs for your existing DynamoDB tables. You should look at the following strategies to see which cost optimization strategy best suits your needs and approach them iteratively. Each strategy will provide an overview of what might be impacting your costs, what signs to look for, and prescriptive guidance on how to reduce them.

**Topics**
+ [Evaluate your costs at the table level](CostOptimization_TableLevelCostAnalysis.md)
+ [Evaluate your DynamoDB table's capacity mode](CostOptimization_TableCapacityMode.md)
+ [Evaluate your DynamoDB table's auto scaling settings](CostOptimization_AutoScalingSettings.md)
+ [Evaluate your DynamoDB table class selection](CostOptimization_TableClass.md)
+ [Identify your unused resources in DynamoDB](CostOptimization_UnusedResources.md)
+ [Evaluate your DynamoDB table usage patterns](CostOptimization_TableUsagePatterns.md)
+ [Evaluate your DynamoDB streams usage](CostOptimization_StreamsUsage.md)
+ [Evaluate your provisioned capacity for right-sized provisioning in your DynamoDB table](CostOptimization_RightSizedProvisioning.md)

# Evaluate your costs at the table level
<a name="CostOptimization_TableLevelCostAnalysis"></a>

 The Cost Explorer tool found within the AWS Management Console allows you to see costs broken down by type, such as read, write, storage and backup charges. You can also see these costs summarized by period such as month or day.

One challenge administrators can face is when the costs of only one particular table need to be reviewed. Some of this data is available via the DynamoDB console or via calls to the `DescribeTable` API, however Cost Explorer does not, by default, allow you to filter or group by costs associated with a specific table. This section will show you how to use tagging to perform individual table cost analysis in Cost Explorer.

**Topics**
+ [How to view the costs of a single DynamoDB table](#CostOptimization_TableLevelCostAnalysis_ViewInfo)
+ [Cost Explorer's default view](#CostOptimization_TableLevelCostAnalysis_CostExplorer)
+ [How to use and apply table tags in Cost Explorer](#CostOptimization_TableLevelCostAnalysis_Tagging)

## How to view the costs of a single DynamoDB table
<a name="CostOptimization_TableLevelCostAnalysis_ViewInfo"></a>

Both the Amazon DynamoDB AWS Management Console and the `DescribeTable` API will show you information about a single table, including the primary key schema, any indexes on the table, and the size and item count of the table and any indexes. The size of the table, plus the size of the indexes, can be used to calculate the monthly storage cost for your table. For example, \$10.25 per GB in the us-east-1 region.

If the table is in provisioned capacity mode, the current RCU and WCU settings are returned as well. These could be used to calculate the current read and write costs for the table, but these costs could change, especially if the table has been configured with Auto Scaling.

**Note**  
If the table is in on-demand capacity mode, then `DescribeTable` will not help estimate throughput costs, as these are billed based on actual, not provisioned usage in any one period.

## Cost Explorer's default view
<a name="CostOptimization_TableLevelCostAnalysis_CostExplorer"></a>

Cost Explorer's default view provides charts showing the cost of consumed resources such as throughput and storage. You can choose to group costs by period, such as totals by month or by day. The costs of storage, reads, writes, and other features can be broken out and compared as well.

![\[Cost Explorer's default view showing the cost of consumed resources grouped by usage type.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/CostExplorerView.png)


## How to use and apply table tags in Cost Explorer
<a name="CostOptimization_TableLevelCostAnalysis_Tagging"></a>

By default, Cost Explorer does not provide a summary of the costs for any one specific table, as it will combine the costs of multiple tables into a total. However, you can use [AWS resource tagging](https://docs.aws.amazon.com/general/latest/gr/aws_tagging.html) to identify each table by a metadata tag. Tags are key-value pairs you can use for a variety of purposes, such as to identify all resources belonging to a project or department. For this example, we'll assume you have a table named **MyTable**.

1. Set a tag with the key of **table\$1name** and the value of **MyTable**.

1. [Activate the tag within Cost Explorer](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/activating-tags.html) and then filter on the tag value to gain more visibility into each table's costs.

**Note**  
It may take one or two days for the tag to start appearing in Cost Explorer

You can set metadata tags yourself in the console, or via automation such as the AWS CLI or AWS SDK. Consider requiring a **table\$1name** tag to be set as part of your organization’s new table creation process. For existing tables, there is a Python utility available that will find and apply these tags to all existing tables in a certain region in your account. See [Eponymous Table Tagger on GitHub](https://github.com/awslabs/amazon-dynamodb-tools#eponymous-table-tagger-tool) for more details.

# Evaluate your DynamoDB table's capacity mode
<a name="CostOptimization_TableCapacityMode"></a>

This section provides an overview of how to select the appropriate capacity mode for your DynamoDB table. Each mode is tuned to meet the needs of a different workload in terms of responsiveness to change in throughput, as well as how that usage is billed. You must balance these factors when making your decision.

**Topics**
+ [What table capacity modes are available](#CostOptimization_TableCapacityMode_Overview)
+ [When to select on-demand capacity mode](#CostOptimization_TableCapacityMode_OnDemand)
+ [When to select provisioned capacity mode](#CostOptimization_TableCapacityMode_Provisioned)
+ [Additional factors to consider when choosing a table capacity mode](#CostOptimization_TableCapacityMode_AdditionalFactors)

## What table capacity modes are available
<a name="CostOptimization_TableCapacityMode_Overview"></a>

When you create a DynamoDB table, you must select either on-demand or provisioned capacity mode. 

You can switch tables from provisioned capacity mode to on-demand mode up to four times in a 24-hour rolling window. You can switch tables from on-demand mode to provisioned capacity mode at any time. 

**On-demand capacity mode**  
The [on-demand capacity mode](on-demand-capacity-mode.md) is designed to eliminate the need to plan or provision the capacity of your DynamoDB table. In this mode, your table will instantly accommodate requests to your table without the need to scale any resources up or down (up to twice the previous peak throughput of the table).

DynamoDB on-demand offers pay-per-request pricing for read and write requests so that you only pay for what you use.

**Provisioned capacity mode**  
The [provisioned capacity](provisioned-capacity-mode.md) mode is a more traditional model where you must define how much capacity the table has available for requests either directly or with the assistance of auto scaling. Because a specific capacity is provisioned for the table at any given time, billing is based off of the total capacity provisioned rather than the number of requests consumed. Going over the allocated capacity can also cause the table to reject requests and reduce the experience of your applications users.

Provisioned capacity mode requires constant monitoring to find a balance between not over-provisioning or under-provisioning the table to keep both throttling low and costs tuned.

## When to select on-demand capacity mode
<a name="CostOptimization_TableCapacityMode_OnDemand"></a>

When optimizing for cost, on-demand mode is your best choice when you have a workload similar to the following graphs.

The following factors contribute to this type of workload:
+ Traffic pattern that evolves over time 
+ Variable volume of requests (resulting from batch workloads)
+ Unpredictable request timing (resulting in traffic spikes)
+ Drops to zero or below 30% of the peak for a given hour 

![\[Graphs for unpredictable, variable workload with spikes and periods of low activity.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/choose-on-demand-1.png)![\[Graphs for unpredictable, variable workload with spikes and periods of low activity.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/choose-on-demand-2.png)


For workloads with the above factors, using auto scaling to maintain enough capacity on the table to respond to spikes in traffic will likely lead to the table being overprovisioned and costing more than necessary or the table being under provisioned and requests being unnecessarily throttled. On-demand capacity mode is the better choice because it can handle fluctuating traffic without requiring you to predict or adjust capacity.

With on-demand mode’s pay-per-request pricing model, you don’t have to worry about idle capacity because you only pay for the throughput you actually use. You are billed per read or write request consumed, so your costs directly reflect your actual usage, making it easy to balance costs and performance. Optionally, you can also configure maximum read or write (or both) throughput per second for individual on-demand tables and global secondary indexes to help keep costs and usage bounded. For more information, see [maximum throughput for on-demand tables](on-demand-capacity-mode-max-throughput.md) .

## When to select provisioned capacity mode
<a name="CostOptimization_TableCapacityMode_Provisioned"></a>

An ideal workload for provisioned capacity mode is one with a more steady and predictable usage pattern like the graph below.

**Note**  
We recommend reviewing metrics at a fine-grained period, such as 14 days, before taking action on your provisioned capacity.

The following factors contribute to this type of workload:
+ Steady, predictable and cyclical traffic for a given hour or day
+ Limited short-term bursts of traffic

![\[Graph depicting a predictable, cyclical workload with limited spikes in traffic.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/choose-provisioned-1.png)


Since the traffic volumes within a given hour or day are more stable, you can set the provisioned capacity of the table relatively close to the actual consumed capacity of the table. Cost optimizing a provisioned capacity table is ultimately an exercise in getting the provisioned capacity (blue line) as close to the consumed capacity (orange line) as possible without increasing `ThrottledRequests` on the table. The space between the two lines is both wasted capacity as well as insurance against a bad user experience due to throttling. If you can predict your application’s throughput requirements and you prefer the cost predictability of controlling read and write capacity, then you may want to continue using provisioned tables.

DynamoDB provides auto scaling for provisioned capacity tables which will automatically balance this on your behalf. This lets you track your consumed capacity throughout the day and set the capacity of the table based on a handful of variables. When using auto scaling, your table will be over-provisioned and you need to fine tune the ratio between number of throttles versus over-provisioned capacity units to match your workload needs.

![\[DynamoDB console. Provisioned capacity and auto scaling are enabled. Target utilization is set to 70.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/TableCapacityModeAutoScaling.png)


**Minimum capacity units**  
You can set the minimum capacity of a table to limit throttling, but it will not reduce the cost of the table. If your table has periods of low usage follow by a sudden burst of high usage, setting the minimum can prevent auto scaling from setting the table capacity too low.

**Maximum capacity units**  
You can set the maximum capacity of a table to limit a table scaling higher than intended. Consider applying a maximum for Dev or Test tables where large-scale load testing is not desired. You can set a maximum for any table, but be sure to regularly evaluate this setting against the table baseline when using it in Production to prevent accidental throttling.

**Target utilization**  
Setting the target utilization of the table is the primary means of cost optimization for a provisioned capacity table. Setting a lower percent value here will increase how much the table is overprovisioned, increasing cost, but reducing the risk of throttling. Setting a higher percent value will decrease how much the table is overprovisioned, but increase the risk of throttling.

## Additional factors to consider when choosing a table capacity mode
<a name="CostOptimization_TableCapacityMode_AdditionalFactors"></a>

When deciding between the two modes, there are some additional factors worth considering.

**Provisioned capacity utilization**  
To determine when on-demand mode would cost less than provisioned capacity, it's helpful to look at your provisioned capacity utilization, which refers to how efficiently the allocated (or “provisioned) resources are being used. On-demand mode costs less for workloads with average provisioned capacity utilization below 35%. In many cases, even for workloads with provisioned capacity utilization higher than 35%, it can be more cost-effective to use on-demand mode especially if the workload has periods of low activity mixed with occasional peaks.

**Reserved capacity**  
For provisioned capacity tables, DynamoDB offers the ability to purchase reserved capacity for your read and write capacity (replicated write capacity units (rWCU) and Standard-IA tables are currently not eligible). Reserved capacity offers significant discounts over standard provisioned capacity pricing.

 When deciding between the two table modes, consider how much this additional discount will affect the cost of the table. In some cases, it may cost less to run a relatively unpredictable workload can be cheaper to run on an overprovisioned provisioned capacity table with reserved capacity. 

**Improving predictability of your workload**  
In some situations, a workload may seemingly have both a predictable and unpredictable pattern. While this can be easily supported with an on-demand table, costs will likely be better if the unpredictable patterns in the workload can be improved.

One of the most common causes of these patterns is batch imports. This type of traffic can often exceed the baseline capacity of the table to such a degree that throttling would occur if it were to run. To keep a workload like this running on a provisioned capacity table, consider the following options:
+ If the batch occurs at scheduled times, you can schedule an increase to your auto- scaling capacity before it runs
+ If the batch occurs randomly, consider trying to extend the time it runs rather than executing as fast as possible
+ Add a ramp up period to the import where the velocity of the import starts small but is slowly increased over a few minutes until auto scaling has had the opportunity to start adjusting table capacity

# Evaluate your DynamoDB table's auto scaling settings
<a name="CostOptimization_AutoScalingSettings"></a>

This section provides an overview of how to evaluate the auto scaling settings on your DynamoDB tables. [Amazon DynamoDB auto scaling](AutoScaling.md) is a feature that manages table and global secondary index (GSI) throughput based on your application traffic and your target utilization metric. This ensures your tables or GSIs will have the required capacity required for your application patterns.

The AWS auto scaling service will monitor your current table utilization and compare it to the target utilization value: `TargetValue`. It will notify you if it is time to increase or decrease the capacity allocated. 

**Topics**
+ [Understanding your auto scaling settings](#CostOptimization_AutoScalingSettings_UnderProvisionedTables)
+ [How to identify tables with low target utilization (<=50%)](#CostOptimization_AutoScalingSettings_IdentifyLowUtilization)
+ [How to address workloads with seasonal variance](#CostOptimization_AutoScalingSettings_SeasonalVariance)
+ [How to address spiky workloads with unknown patterns](#CostOptimization_AutoScalingSettings_UnknownPatterns)
+ [How to address workloads with linked applications](#CostOptimization_AutoScalingSettings_BetweenRanges)

## Understanding your auto scaling settings
<a name="CostOptimization_AutoScalingSettings_UnderProvisionedTables"></a>

Defining the correct value for the target utilization, initial step, and final values is an activity that requires involvement from your operations team. This allows you to properly define the values based on historical application usage, which will be used to trigger the AWS auto scaling policies. The utilization target is the percentage of your total capacity that needs to be hit during a period of time before the auto scaling rules apply.

When you set a **high utilization target (a target around 90%)** it means your traffic needs to be higher than 90% for a period of time before the auto scaling kicks in. You should not use a high utilization target unless your application is very constant and doesn’t receive spikes in traffic.

When you set a very **low utilization (a target less than 50%)** it means your application would need to reach 50% of the provisioned capacity before it triggers an auto scaling policy. Unless your application traffic grows at a very aggressive rate, this usually translates into unused capacity and wasted resources. 

## How to identify tables with low target utilization (<=50%)
<a name="CostOptimization_AutoScalingSettings_IdentifyLowUtilization"></a>

You can use either the AWS CLI or AWS Management Console to monitor and identify the `TargetValues` for your auto scaling policies in your DynamoDB resources:

------
#### [ AWS CLI ]

1. Return the entire list of resources by running the following command:

   ```
   aws application-autoscaling describe-scaling-policies --service-namespace dynamodb
   ```

   This command will return the entire list of auto scaling policies that are issued to any DynamoDB resource. If you only want to retrieve the resources from a particular table, you can add the `–resource-id parameter`. For example:

   ```
   aws application-autoscaling describe-scaling-policies --service-namespace dynamodb --resource-id "table/<table-name>”
   ```

1. Return only the auto scaling policies for a particular GSI by running the following command

   ```
   aws application-autoscaling describe-scaling-policies --service-namespace dynamodb --resource-id "table/<table-name>/index/<gsi-name>”
   ```

   The values we're interested in for the auto scaling policies are highlighted below. We want to ensure that the target value is greater than 50% to avoid over-provisioning. You should obtain a result similar to the following:

   ```
   {
       "ScalingPolicies": [
           {
               "PolicyARN": "arn:aws:autoscaling:<region>:<account-id>:scalingPolicy:<uuid>:resource/dynamodb/table/<table-name>/index/<index-name>:policyName/$<full-gsi-name>-scaling-policy",
               "PolicyName": $<full-gsi-name>”,
               "ServiceNamespace": "dynamodb",
               "ResourceId": "table/<table-name>/index/<index-name>",
               "ScalableDimension": "dynamodb:index:WriteCapacityUnits",
               "PolicyType": "TargetTrackingScaling",
               "TargetTrackingScalingPolicyConfiguration": {
                   "TargetValue": 70.0,
                   "PredefinedMetricSpecification": {
                       "PredefinedMetricType": "DynamoDBWriteCapacityUtilization"
                   }
               },
               "Alarms": [
                   ...
               ],
               "CreationTime": "2022-03-04T16:23:48.641000+10:00"
           },
           {
               "PolicyARN": "arn:aws:autoscaling:<region>:<account-id>:scalingPolicy:<uuid>:resource/dynamodb/table/<table-name>/index/<index-name>:policyName/$<full-gsi-name>-scaling-policy",
               "PolicyName":$<full-gsi-name>”,
               "ServiceNamespace": "dynamodb",
               "ResourceId": "table/<table-name>/index/<index-name>",
               "ScalableDimension": "dynamodb:index:ReadCapacityUnits",
               "PolicyType": "TargetTrackingScaling",
               "TargetTrackingScalingPolicyConfiguration": {
                   "TargetValue": 70.0,
                   "PredefinedMetricSpecification": {
                       "PredefinedMetricType": "DynamoDBReadCapacityUtilization"
                   }
               },
               "Alarms": [
                   ...
               ],
               "CreationTime": "2022-03-04T16:23:47.820000+10:00"
           }
       ]
   }
   ```

------
#### [ AWS Management Console ]

1. Sign in to the AWS Management Console and open the DynamoDB console at [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/).

   Select an appropriate AWS Region if necessary.

1. On the left navigation bar, select **Tables**. On the **Tables** page, select the table's **Name**.

1. On the *Table details* page, choose **Additional settings**, and then review your table's auto scaling settings.  
![\[DynamoDB table details page with auto scaling settings. Review the provisioned capacity utilization and adjust as needed.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings1.png)

   For indexes, expand the **Index capacity** section to review the index's auto scaling settings.  
![\[DynamoDB console's Index capacity section. Review and manage auto scaling settings for indexes.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings2.png)

------

If your target utilization values are less than or equal to 50%, you should explore your table utilization metrics to see if they are [under-provisioned or over-provisioned](CostOptimization_RightSizedProvisioning.md). 

## How to address workloads with seasonal variance
<a name="CostOptimization_AutoScalingSettings_SeasonalVariance"></a>

Consider the following scenario: your application is operating under a minimum average value most of the time, but the utilization target is low so your application can react quickly to events that happen at certain hours in the day and you have enough capacity and avoid getting throttled. This scenario is common when you have an application that is very busy during normal office hours (9 AM to 5 PM) but then it works at a base level during after hours. Since some users will start to connect before 9 am, the application uses this low threshold to ramp up quickly to get to the *required* capacity during peak hours.

This scenario could look like this: 
+ Between 5 PM and 9 AM the `ConsumedWriteCapacity` units stay between 90 and 100
+ Users start to connect to the application before 9 AM and the capacity units increases considerably (the maximum value you’ve seen is 1500 WCU)
+ On average, your application usage varies between 800 to 1200 during working hours

If the previous scenario applies to you, consider using [scheduled auto scaling](https://docs.aws.amazon.com/autoscaling/application/userguide/examples-scheduled-actions.html), where your table could still have an application auto scaling rule configured, but with a less aggressive target utilization that only provisions the extra capacity at the specific intervals you require.

You can use AWS CLI to execute the following steps to create a scheduled auto scaling rule that will execute based on the time of day and the day of the week.

1. Register your DynamoDB table or GSI as scalable target with Application Auto Scaling. A scalable target is a resource that Application Auto Scaling can scale out or in.

   ```
   aws application-autoscaling register-scalable-target \
       --service-namespace dynamodb \
       --scalable-dimension dynamodb:table:WriteCapacityUnits \
       --resource-id table/<table-name> \
       --min-capacity 90 \
       --max-capacity 1500
   ```

1. Set up scheduled actions according to your requirements.

   We'll need two rules to cover the scenario: one to scale up and another to scale down. The first rule to scale up the scheduled action:

   ```
   aws application-autoscaling put-scheduled-action \
       --service-namespace dynamodb \
       --scalable-dimension dynamodb:table:WriteCapacityUnits \
       --resource-id table/<table-name> \
       --scheduled-action-name my-8-5-scheduled-action \
       --scalable-target-action MinCapacity=800,MaxCapacity=1500 \
       --schedule "cron(45 8 ? * MON-FRI *)" \
       --timezone "Australia/Brisbane"
   ```

   The second rule to scale down the scheduled action:

   ```
   aws application-autoscaling put-scheduled-action \
       --service-namespace dynamodb \
       --scalable-dimension dynamodb:table:WriteCapacityUnits \
       --resource-id table/<table-name> \
       --scheduled-action-name my-5-8-scheduled-down-action \
       --scalable-target-action MinCapacity=90,MaxCapacity=1500 \
       --schedule "cron(15 17 ? * MON-FRI *)" \
       --timezone "Australia/Brisbane"
   ```

1. Run the following command to validate both rules have been activated:

   ```
   aws application-autoscaling describe-scheduled-actions --service-namespace dynamodb
   ```

   You should get a result like this:

   ```
   {
       "ScheduledActions": [
           {
               "ScheduledActionName": "my-5-8-scheduled-down-action",
               "ScheduledActionARN": "arn:aws:autoscaling:<region>:<account>:scheduledAction:<uuid>:resource/dynamodb/table/<table-name>:scheduledActionName/my-5-8-scheduled-down-action",
               "ServiceNamespace": "dynamodb",
               "Schedule": "cron(15 17 ? * MON-FRI *)",
               "Timezone": "Australia/Brisbane",
               "ResourceId": "table/<table-name>",
               "ScalableDimension": "dynamodb:table:WriteCapacityUnits",
               "ScalableTargetAction": {
                   "MinCapacity": 90,
                   "MaxCapacity": 1500
               },
               "CreationTime": "2022-03-15T17:30:25.100000+10:00"
           },
           {
               "ScheduledActionName": "my-8-5-scheduled-action",
               "ScheduledActionARN": "arn:aws:autoscaling:<region>:<account>:scheduledAction:<uuid>:resource/dynamodb/table/<table-name>:scheduledActionName/my-8-5-scheduled-action",
               "ServiceNamespace": "dynamodb",
               "Schedule": "cron(45 8 ? * MON-FRI *)",
               "Timezone": "Australia/Brisbane",
               "ResourceId": "table/<table-name>",
               "ScalableDimension": "dynamodb:table:WriteCapacityUnits",
               "ScalableTargetAction": {
                   "MinCapacity": 800,
                   "MaxCapacity": 1500
               },
               "CreationTime": "2022-03-15T17:28:57.816000+10:00"
           }
       ]
   }
   ```

The following picture shows a sample workload that always keeps the 70% target utilization. Notice how the auto scaling rules are still applying and the throughput will not be reduced.

![\[A table's throughput at 70% target utilization, even as auto scaling rules adjust capacity.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings3.png)


Zooming in, we can see there was a spike in the application that triggered the 70% auto scaling threshold, forcing the auto scaling to kick in and provide the extra capacity required for the table. The scheduled auto scaling action will affect maximum and minimum values, and it is your responsibility to set them up.

![\[Spike in a DynamoDB table throughput that initiates auto scaling to provide required extra capacity.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings4.png)


![\[DynamoDB table's auto scaling configuration: Target utilization and minimum and maximum capacity values.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings5.png)


## How to address spiky workloads with unknown patterns
<a name="CostOptimization_AutoScalingSettings_UnknownPatterns"></a>

In this scenario, the application uses a very low utilization target because you don’t know the application patterns yet, and you want to ensure your workload is not throttled.

Consider using [on-demand capacity mode](capacity-mode.md#capacity-mode-on-demand) instead. On-demand tables are perfect for spiky workloads where you don’t know the traffic patterns. With on-demand capacity mode, you pay per request for the data reads and writes your application performs on your tables. You do not need to specify how much read and write throughput you expect your application to perform, as DynamoDB instantly accommodates your workloads as they ramp up or down.

## How to address workloads with linked applications
<a name="CostOptimization_AutoScalingSettings_BetweenRanges"></a>

In this scenario, the application depends on other systems, like batch processing scenarios where you can have big spikes in traffic according to events in the application logic.

Consider developing custom auto scaling logic that reacts to those events where you can increase table capacity and `TargetValues` depending on your specific needs. You could benefit from Amazon EventBridge and use a combination of AWS services like Lambda and Step Functions to react to your specific application needs.

# Evaluate your DynamoDB table class selection
<a name="CostOptimization_TableClass"></a>

This section provides an overview of how to select the appropriate table class for your DynamoDB table. With the launch of the Standard Infrequent-Access (Standard-IA) table class, you now have the ability to optimize a table for either lower storage cost or lower throughput cost.

**Topics**
+ [What table classes are available](#CostOptimization_TableClass_Overview)
+ [When to select the DynamoDB Standard table class](#CostOptimization_TableClass_Standard)
+ [When to select DynamoDB Standard-IA table class](#CostOptimization_TableClass_StandardIA)
+ [Additional factors to consider when choosing a table class](#CostOptimization_TableClass_AdditionalFactors)

## What table classes are available
<a name="CostOptimization_TableClass_Overview"></a>

When you create a DynamoDB Table, you must select either DynamoDB Standard or DynamoDB Standard-IA for the table class. The table class can be changed twice in a 30-day period, so you can always change it in the future. Selecting either table class has no effect on table performance, availability, reliability, or durability.

![\[DynamoDB table class options. In this image, the DynamoDB Standard-IA table class is selected.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/TableClassOptions.png)


**Standard table class**  
The Standard table class is the default option for new tables. This option maintains the original billing balance of DynamoDB which offers a balance of throughput and storage costs for tables with frequently accessed data.

**Standard-IA table class**  
The Standard-IA table class offers a lower storage cost (\$160% lower) for workloads that require long-term storage of data with infrequent updates or reads. Since the class is optimized for infrequent access, reads and writes will be billed at a slightly higher cost (\$125% higher) than the Standard table class.

## When to select the DynamoDB Standard table class
<a name="CostOptimization_TableClass_Standard"></a>

DynamoDB Standard table class is best suited for tables whose storage cost is approximately 50% or less of the overall monthly cost of the table. This cost balance is indicative of a workload that regularly accesses or updates items already stored within DynamoDB.

## When to select DynamoDB Standard-IA table class
<a name="CostOptimization_TableClass_StandardIA"></a>

DynamoDB Standard-IA table class is best suited for tables whose storage cost is approximately 50% or more of the overall monthly cost of the table. This cost balance is indicative of a workload that creates or reads fewer items per month than it keeps in storage.

 A common use for the Standard-IA table class is moving less frequently accessed data to individual Standard-IA tables. For further information, see [ Optimizing the storage costs of your workloads with Amazon DynamoDB Standard-IA table class](https://aws.amazon.com/blogs/database/optimize-the-storage-costs-of-your-workloads-with-amazon-dynamodb-standard-ia-table-class/).

## Additional factors to consider when choosing a table class
<a name="CostOptimization_TableClass_AdditionalFactors"></a>

When deciding between the two table classes, there are some additional factors worth considering as part of your decision.

**Reserved capacity**  
Purchasing reserved capacity for tables using the Standard-IA table class is currently not supported. When transitioning from a Standard table with reserved capacity to a Standard-IA table without reserved capacity, you may not see a cost benefit.

# Identify your unused resources in DynamoDB
<a name="CostOptimization_UnusedResources"></a>

This section provides an overview of how to evaluate your unused resources regularly. As your application requirements evolve you should ensure no resources are unused and contributing to unnecessary Amazon DynamoDB costs. The procedures described below will use Amazon CloudWatch metrics to identify unused resources and will help you identify and take action on those resources to reduce costs.

You can monitor DynamoDB using CloudWatch, which collects and processes raw data from DynamoDB into readable, near real-time metrics. These statistics are retained for a period of time, so that you can access historical information to better understand your utilization. By default, DynamoDB metric data is sent to CloudWatch automatically. For more information, see [What is Amazon CloudWatch?](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html) and [Metrics retention](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#metrics-retention) in the *Amazon CloudWatch User Guide*. 

**Topics**
+ [How to identify unused resources](#CostOptimization_UnusedResources_Identifying)
+ [Identifying unused table resources](#CostOptimization_UnusedResources_Tables)
+ [Cleaning up unused table resources](#CostOptimization_UnusedResources_Tables_Cleanup)
+ [Identifying unused GSI resources](#CostOptimization_UnusedResources_GSI)
+ [Cleaning up unused GSI resources](#CostOptimization_UnusedResources_GSI_Cleanup)
+ [Cleaning up unused global tables](#CostOptimization_UnusedResources_GlobalTables)
+ [Cleaning up unused backups or point-in-time recovery (PITR)](#CostOptimization_UnusedResources_Backups)

## How to identify unused resources
<a name="CostOptimization_UnusedResources_Identifying"></a>

To identify unused tables or indexes, we'll look at the following CloudWatch metrics over a period of 30 days to understand if there are any active reads or writes on the table or any reads on the global secondary indexes (GSIs):

**[ConsumedReadCapacityUnits](metrics-dimensions.md#ConsumedReadCapacityUnits)**  
The number of read capacity units consumed over the specified time period, so you can track how much consumed capacity you have used. You can retrieve the total consumed read capacity for a table and all of its global secondary indexes, or for a particular global secondary index.

**[ConsumedWriteCapacityUnits](metrics-dimensions.md#ConsumedWriteCapacityUnits)**  
The number of write capacity units consumed over the specified time period, so you can track how much consumed capacity you have used. You can retrieve the total consumed write capacity for a table and all of its global secondary indexes, or for a particular global secondary index.

## Identifying unused table resources
<a name="CostOptimization_UnusedResources_Tables"></a>

Amazon CloudWatch is a monitoring and observability service which provides the DynamoDB table metrics you’ll use to identify unused resources. CloudWatch metrics can be viewed through the AWS Management Console as well as through the AWS Command Line Interface.

------
#### [ AWS Command Line Interface ]

To view your tables metrics through the AWS Command Line Interface, you can use the following commands.

1. First, evaluate your table's reads:

   ```
   aws cloudwatch get-metric-statistics --metric-name
   ConsumedReadCapacityUnits --start-time <start-time> --end-time <end-
   time> --period <period> --namespace AWS/DynamoDB --statistics Sum --
   dimensions Name=TableName,Value=<table-name>
   ```

   To avoid falsely identifying a table as unused, evaluate metrics over a longer period. Choose an appropriate start-time and end-time range, such as **30 days**, and an appropriate period, such as **86400**.

   In the returned data, any **Sum** above **0** indicates that the table you are evaluating received read traffic during that period.

   The following result shows a table receiving read traffic in the evaluated period:

   ```
           {
               "Timestamp": "2022-08-25T19:40:00Z",
               "Sum": 36023355.0,
               "Unit": "Count"
           },
           {
               "Timestamp": "2022-08-12T19:40:00Z",
               "Sum": 38025777.5,
               "Unit": "Count"
           },
   ```

   The following result shows a table not receiving read traffic in the evaluated period:

   ```
           {
               "Timestamp": "2022-08-01T19:50:00Z",
               "Sum": 0.0,
               "Unit": "Count"
           },
           {
               "Timestamp": "2022-08-20T19:50:00Z",
               "Sum": 0.0,
               "Unit": "Count"
           },
   ```

1. Next, evaluate your table’s writes:

   ```
   aws cloudwatch get-metric-statistics --metric-name
   ConsumedWriteCapacityUnits --start-time <start-time> --end-time <end-
   time> --period <period> --namespace AWS/DynamoDB --statistics Sum --
   dimensions Name=TableName,Value=<table-name>
   ```

   To avoid falsely identifying a table as unused, you will want to evaluate metrics over a longer period. Choose an appropriate start-time and end-time range, such as **30 days**, and an appropriate period, such as **86400**.

   In the returned data, any **Sum** above **0** indicates that the table you are evaluating received read traffic during that period.

   The following result shows a table receiving write traffic in the evaluated period:

   ```
           {
               "Timestamp": "2022-08-19T20:15:00Z",
               "Sum": 41014457.0,
               "Unit": "Count"
           },
           {
               "Timestamp": "2022-08-18T20:15:00Z",
               "Sum": 40048531.0,
               "Unit": "Count"
           },
   ```

   The following result shows a table not receiving write traffic in the evaluated period:

   ```
           {
               "Timestamp": "2022-07-31T20:15:00Z",
               "Sum": 0.0,
               "Unit": "Count"
           },
           {
               "Timestamp": "2022-08-19T20:15:00Z",
               "Sum": 0.0,
               "Unit": "Count"
           },
   ```

------
#### [ AWS Management Console ]

The following steps will allow you to evaluate your resources utilization through the AWS Management Console.

1. Log into the AWS console and navigate to the CloudWatch service page at [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/). Select the appropriate AWS region in the top right of the console, if necessary.

1. On the left navigation bar, locate the Metrics section and select **All metrics**.

1. The action above will open a dashboard with two panels. In the top panel you will see currently graphed metrics. In the bottom you will select the metrics available to graph. Select DynamoDB in the bottom panel.

1. In the DynamoDB metrics selection panel select the **Table Metrics** category to show the metrics for your tables in the current region.

1. Identify your table name by scrolling down the menu, then select the metrics `ConsumedReadCapacityUnits` and `ConsumedWriteCapacityUnits` for your table.

1. Select the **Graphed metrics (2)** tab and adjust the **Statistic** column to **Sum**.  
![\[Graphed metrics tab. Statistic is set to Sum to view resource usage data in the console.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/GraphedMetricsTab.png)

1. To avoid falsely identifying a table as unused, you'll want to evaluate metrics over a longer period. At the top of the graph panel choose an appropriate time frame, such as 1 month, to evaluate your table. Select **Custom**, select **1 Months** in the dropdowns, and choose **Apply**.  
![\[CloudWatch console. Custom time frame of 1 month is selected to evaluate metrics.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/OneMonthTimeFrame.png)

1. Evaluate the graphed metrics for your table to determine if it is being used. Metrics that have gone above **0 **indicate that a table has been used during the evaluated time period. A flat graph at **0 **for both read and write indicates a table that is unused.

   The following image shows a table with read traffic:  
![\[Graph showing the ConsumedReadCapacityUnits for a DynamoDB table, suggesting the table is in use.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/TableWithReadTraffic.png)

   The following image shows a table without read traffic:  
![\[Graph showing no read activity for a DynamoDB table, suggesting the table isn't in use.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/TableWithoutReadTraffic.png)

------

## Cleaning up unused table resources
<a name="CostOptimization_UnusedResources_Tables_Cleanup"></a>

If you have identified unused table resources, you can reduce their ongoing costs in the following ways.

**Note**  
If you have identified an unused table but would still like to keep it available in case it needs to be accessed in the future, consider switching it to on-demand mode. Otherwise, you can consider backing up and deleting the table entirely.

**Capacity modes**  
DynamoDB charges for reading, writing, and storing data in your DynamoDB tables.

DynamoDB has [two capacity modes](capacity-mode.md), which come with specific billing options for processing reads and writes on your tables: on-demand and provisioned. The read/write capacity mode controls how you are charged for read and write throughput and how you manage capacity.

For on-demand mode tables, you don't need to specify how much read and write throughput you expect your application to perform. DynamoDB charges you for the reads and writes that your application performs on your tables in terms of read request units and write request units. If there is no activity on your table/index you do not pay for throughput but you’ll still incur a storage charge.

**Table class**  
DynamoDB also offers [two table classes](HowItWorks.TableClasses.md) designed to help you optimize for cost. The DynamoDB Standard table class is the default and is recommended for most workloads. The DynamoDB Standard-Infrequent Access (DynamoDB Standard-IA) table class is optimized for tables where storage is the dominant cost.

If there is no activity on your table or index, storage is likely to be the dominant cost and changing table class will offer a significant savings.

**Deleting tables**  
If you’ve discovered an unused table and would like to delete it, you may wish to make a backup or export of the data first.

Backups taken through AWS Backup can leverage cold storage tiering, further reducing costs. Refer to the [Using AWS Backup with DynamoDB](backuprestore_HowItWorksAWS.md) documentation for information on how enable backups through AWS Backup as well as the [Managing backup plans](https://docs.aws.amazon.com/aws-backup/latest/devguide/about-backup-plans) documentation for information on how to use lifecycle to move your backup to cold storage.

Alternatively, you may choose to export your table’s data to S3. To do so, refer to the [Export to Amazon S3](S3DataExport.HowItWorks.md) documentation. Once your data is exported, if you wish to leverage S3 Glacier Instant Retrieval, S3 Glacier Flexile Retrieval, or S3 Glacier Deep Archive to further reduce costs, see [Managing your storage lifecycle](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt).

After your table has been backed up, you may choose to delete it either through the AWS Management Console or through the AWS Command Line Interface.

## Identifying unused GSI resources
<a name="CostOptimization_UnusedResources_GSI"></a>

The steps for identifying an unused global secondary are similar to those for identifying an unused table. Since DynamoDB replicates items written to your base table into your GSI if they contain the attribute used as the GSI’s partition key, an unused GSI is still likely to have `ConsumedWriteCapacityUnits` above 0 if its base table is in use. As a result, you’ll be evaluating only the `ConsumedReadCapacityUnits` metric to determine if your GSI is unused.

To view your GSI metrics through the AWS AWS CLI, you can use the following commands to evaluate your tables reads:

```
aws cloudwatch get-metric-statistics --metric-name
ConsumedReadCapacityUnits --start-time <start-time> --end-time <end-
time> --period <period> --namespace AWS/DynamoDB --statistics Sum --
dimensions Name=TableName,Value=<table-name>
Name=GlobalSecondaryIndexName,Value=<index-name>
```

To avoid falsely identifying a table as unused, you will want to evaluate metrics over a longer period. Choose an appropriate start-time and end-time range, such as 30 days, and an appropriate period, such as 86400.

In the returned data, any Sum above 0 indicates that the table you are evaluating received read traffic during that period.

The following result shows a GSI receiving read traffic in the evaluated period:

```
        {
          "Timestamp": "2022-08-17T21:20:00Z",
          "Sum": 36319167.0,
          "Unit": "Count"
        },
        {
          "Timestamp": "2022-08-11T21:20:00Z",
          "Sum": 1869136.0,
          "Unit": "Count"
        },
```

The following result shows a GSI receiving minimal read traffic in the evaluated period:

```
        {
          "Timestamp": "2022-08-28T21:20:00Z",
          "Sum": 0.0,
          "Unit": "Count"
        },
        {
          "Timestamp": "2022-08-15T21:20:00Z",
          "Sum": 2.0,
          "Unit": "Count"
        },
```

The following result shows a GSI receiving no read traffic in the evaluated period:

```
        {
          "Timestamp": "2022-08-17T21:20:00Z",
          "Sum": 0.0,
          "Unit": "Count"
        },
        {
          "Timestamp": "2022-08-11T21:20:00Z",
          "Sum": 0.0,
          "Unit": "Count"
        },
```

## Cleaning up unused GSI resources
<a name="CostOptimization_UnusedResources_GSI_Cleanup"></a>

If you've identified an unused GSI, you can choose to delete it. Since all data present in a GSI is also present in the base table, additional backup is not necessary before deleting a GSI. If in the future the GSI is once again needed, it may be added back to the table.

If you have identified an infrequently used GSI, you should consider design changes in your application that would allow you to delete it or reduce its cost. For example, while DynamoDB scans should be used sparingly because they can consume large amounts of system resources, they may be more cost effective than a GSI if the access pattern it supports is used very infrequently.

Additionally, if a GSI is required to support an infrequent access pattern consider projecting a more limited set of attributes. While this may require subsequent queries against the base table to support your infrequent access patterns, it can potentially offer a significant reduction in storage and write costs.

## Cleaning up unused global tables
<a name="CostOptimization_UnusedResources_GlobalTables"></a>

Amazon DynamoDB global tables provide a fully managed solution for deploying a multi-Region, multi-active database, without having to build and maintain your own replication solution.

Global tables are ideal for providing low-latency access to data close to users and as well as a secondary region for disaster recovery.

If the global tables option is enabled for a resource in an effort to provide low-latency access to data but is not part of your disaster recovery strategy, validate that both replicas are actively serving read traffic by evaluating their CloudWatch metrics. If one replica does not serve read traffic, it may be an unused resource.

If global tables are part of your disaster recovery strategy, one replica not receiving read traffic may be expected under an active/standby pattern.

## Cleaning up unused backups or point-in-time recovery (PITR)
<a name="CostOptimization_UnusedResources_Backups"></a>

DynamoDB offers two styles of backup. Point-in-time recovery provides continuous backups for up to 35 days to help you protect against accidental writes or deletes while on-demand backup allows for snapshot creation which can be saved long term. You can set the recovery period to any value between 1 and 35 days. Both types of backups have costs associated with them.

Refer to the documentation for [Backup and restore for DynamoDB](Backup-and-Restore.md) and [Point-in-time backups for DynamoDB](Point-in-time-recovery.md) to determine if your tables have backups enabled that may no longer be needed.

# Evaluate your DynamoDB table usage patterns
<a name="CostOptimization_TableUsagePatterns"></a>

This section provides an overview of how to evaluate if you are efficiently using your DynamoDB tables. There are certain usage patterns which are not optimal for DynamoDB, and they allow room for optimization from both a performance and cost perspective.

**Topics**
+ [Perform fewer strongly-consistent read operations](#CostOptimization_TableUsagePatterns_StronglyConsistentReads)
+ [Perform fewer transactions for read operations](#CostOptimization_TableUsagePatterns_Transactions)
+ [Perform fewer scans](#CostOptimization_TableUsagePatterns_Scans)
+ [Shorten attribute names](#CostOptimization_TableUsagePatterns_AttributeNames)
+ [Enable Time to Live (TTL)](#CostOptimization_TableUsagePatterns_TTL)
+ [Replace global tables with cross-Region backups](#CostOptimization_TableUsagePatterns_GlobalTables)

## Perform fewer strongly-consistent read operations
<a name="CostOptimization_TableUsagePatterns_StronglyConsistentReads"></a>

DynamoDB allows you to configure [read consistency](HowItWorks.ReadConsistency.md) on a per-request basis. Read requests are eventually consistent by default. Eventually consistent reads are charged at 0.5 RCU for upto 4 KB of data.

Most parts of distributed workloads are flexible and can tolerate eventual consistency. However, there can be access patterns requiring strongly consistent reads. Strongly consistent reads are charged at 1 RCU for upto 4 KB of data, essentially doubling your read costs. DynamoDB provides you with the flexibility to use both consistency models on the same table. 

You can evaluate your workload and application code to confirm if strongly consistent reads are used only where required.

## Perform fewer transactions for read operations
<a name="CostOptimization_TableUsagePatterns_Transactions"></a>

DynamoDB allows you to group certain actions in an all-or-nothing manner, which means you have the ability to execute ACID transactions with DynamoDB. However, as is the case with relational databases, it is not necessary to follow this approach for every action.

A [transactional read operation](transaction-apis.md#transaction-capacity-handling.title) of up to 4 KB consumes 2 RCUs as opposed to the default 0.5 RCUs for reading the same amount of data in an eventually consistent manner. The costs are doubled in write operations which means, a transactional write of up to 1 KB equates to 2 WCUs.

To determine if all operations on your tables are transactions, CloudWatch metrics for the table can be filtered down to the transaction APIs. If transaction APIs are the only graphs available under the `SuccessfulRequestLatency` metrics for the table, this would confirm that every operation is a transaction for this table. Alternatively, if the overall capacity utilization trend matches the transaction API trend, consider revisiting the application design as it seems dominated by transactional APIs.

## Perform fewer scans
<a name="CostOptimization_TableUsagePatterns_Scans"></a>

The extensive use of `Scan` operations generally stems from the need to run analytical queries on the DynamoDB data. Running frequent `Scan` operations on large table can be inefficient and expensive.

A better alternative is to use the [Export to S3](S3DataExport.HowItWorks.md#S3DataExport.HowItWorks.title) feature and choosing a point in time to export the table state to S3. AWS offers services like Athena which can then be used to run analytical queries on the data without consuming any capacity from the table.

The frequency for `Scan` operations can be determined using the `SampleCount` statistic under the `SuccessfulRequestLatency` metric for `Scan`. If `Scan` operations are indeed very frequent, the access patterns and data model should be re-evaluated.

## Shorten attribute names
<a name="CostOptimization_TableUsagePatterns_AttributeNames"></a>

The total size of an item in DynamoDB is the sum of its attribute name lengths and values. Having long attribute names not only contributes towards storage costs, but it might also lead to higher RCU/WCU consumption. We recommend that you choose shorter attribute names rather than long ones. Having shorter attribute names can help limit the item size within the next 4KB/1KB boundary after which you would consume additional RCU/WCU to access data.

## Enable Time to Live (TTL)
<a name="CostOptimization_TableUsagePatterns_TTL"></a>

[Time to Live (TTL)](TTL.md#TTL.title) can identify items older than the expiry time that you have set on an item and remove them from the table. If your data grows over time and older data becomes irrelevant, enabling TTL on the table can help trim your data down and save on storage costs.

 Another useful aspect of TTL is that the expired items occur on your DynamoDB streams, so rather than just removing the data from your data, it is possible to consume those items from the stream and archive them to a lower cost storage tier. Additionally, deleting items via TTL comes at no additional cost — it does not consume capacity, and there’s no overhead of designing a clean up application.

## Replace global tables with cross-Region backups
<a name="CostOptimization_TableUsagePatterns_GlobalTables"></a>

[Global tables](GlobalTables.md#GlobalTables.title) allow you to maintain multiple active replica tables in different Regions — they can all accept write operations and replicate data across each other. It is easy to set up replicas and the synchronization is managed for you. The replicas converge to a consistent state using a last writer wins strategy.

If you are using Global tables purely as a part of failover or disaster recovery (DR) strategy, you can replace them with a cross-Region backup copy for relatively lenient recovery point objectives and recovery time objective requirements. If you do not require fast local access and the high availability of five nines, maintaining a global table replica might not be the best approach for failover.

As an alternative, consider using AWS Backup to manage DynamoDB backups. You can schedule regular backups and copy them across Regions to meet DR requirements in a more cost-effective approach compared to using Global tables.

# Evaluate your DynamoDB streams usage
<a name="CostOptimization_StreamsUsage"></a>

This section provides an overview of how to evaluate your DynamoDB Streams usage. There are certain usage patterns which are not optimal for DynamoDB, and they allow room for optimization from both a performance and cost perspective.

You have two native streaming integrations for streaming and event-driven use cases:
+ [Amazon DynamoDB Streams](Streams.md) 
+ [Amazon Kinesis Data Streams](Streams.KCLAdapter.md) 

This page will just focus on cost optimization strategies for these options. If you'd like to instead find out how to choose between the two options, see [Streaming options for change data capture](streamsmain.md#streamsmain.choose).

**Topics**
+ [Optimizing costs for DynamoDB Streams](#CostOptimization_StreamsUsage_Options_DDBStreams)
+ [Optimizing costs for Kinesis Data Streams](#CostOptimization_StreamsUsage_Options_KDS)
+ [Cost optimization strategies for both types of Streams usage](#CostOptimization_StreamsUsage_GuidanceForBoth)

## Optimizing costs for DynamoDB Streams
<a name="CostOptimization_StreamsUsage_Options_DDBStreams"></a>

As mentioned in the [pricing page](https://aws.amazon.com/dynamodb/pricing/on-demand/) for DynamoDB Streams, regardless of the table’s throughput capacity mode, DynamoDB charges on the number of read requests made towards the table’s DynamoDB Stream. Read requests made towards a DynamoDB Stream are different from the read requests made towards a DynamoDB table.

Each read request in terms of the stream is in the form of a `GetRecords` API call that can return up to 1000 records or 1 MB worth of records in the response, whichever is reached first. None of the [other DynamoDB Stream APIs](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations_Amazon_DynamoDB_Streams.html) are charged and DynamoDB Streams are not charged for being idle. In other words, if no read requests are made to a DynamoDB Stream, no charges will be incurred for having a DynamoDB Stream enabled on a table.

Here are a few consumer applications for DynamoDB Streams:
+ AWS Lambda function(s)
+ Amazon Kinesis Data Streams-based applications
+ Customer consumer applications built using an AWS SDK

Read requests made by AWS Lambda-based consumers of DynamoDB Streams are free, whereas calls made by consumers of any other kind are charged. Every month, the first 2,500,000 read requests made by non-Lambda consumers are also free of cost. This applies to all read requests made to any DynamoDB Streams in an AWS Account for each AWS Region.

**Monitoring your DynamoDB Streams usage**  
DynamoDB Streams charges on the billing console are grouped together for all DynamoDB Streams across the AWS Region in an AWS Account. Currently, tagging DynamoDB Streams is not supported, so cost allocation tags cannot be used to identify granular costs for DynamoDB Streams. The volume of `GetRecords` calls can be obtained at the DynamoDB Stream level to compute the charges per stream. The volume is represented by the DynamoDB Stream’s CloudWatch metric `SuccessfulRequestLatency` and its `SampleCount` statistic. This metric will also include `GetRecords` calls made by global tables to perform on-going replication as well as calls made by AWS Lambda consumers, both of which are not charged. For information on other CloudWatch metrics published by DynamoDB Streams, see [DynamoDB Metrics and dimensions](metrics-dimensions.md).

**Using AWS Lambda as the consumer**  
Evaluate if using AWS Lambda functions as the consumers for DynamoDB Streams is feasible because that can eliminate costs associated with reading from the DynamoDB Stream. On the other hand, DynamoDB Streams Kinesis Adapter or SDK based consumer applications will be charged on the number of `GetRecords` calls they make towards the DynamoDB Stream.

Lambda function invocations will be charged based on standard Lambda pricing, however no charges will be incurred by DynamoDB Streams. Lambda will poll shards in your DynamoDB Stream for records at a base rate of 4 times per second. When records are available, Lambda invokes your function and waits for the result. If processing succeeds, Lambda resumes polling until it receives more records.

**Tuning DynamoDB Streams Kinesis Adapter-based consumer applications**  
Since read requests made by non-Lambda based consumers are charged for DynamoDB Streams, it is important to find a balance between the near real-time requirement and the number of times the consumer application must poll the DynamoDB Stream. 

The frequency of polling DynamoDB Streams using a DynamoDB Streams Kinesis Adapter based application is determined by the configured `idleTimeBetweenReadsInMillis` value. This parameter determines the amount of time in milliseconds that the consumer should wait before processing a shard in case the previous `GetRecords` call made to the same shard did not return any records. By default, this value for this parameter is 1000 ms. If near real-time processing is not required, this parameter could be increased to have the consumer application make fewer `GetRecords` calls and optimize on DynamoDB Streams calls.

## Optimizing costs for Kinesis Data Streams
<a name="CostOptimization_StreamsUsage_Options_KDS"></a>

When a Kinesis Data Stream is set as the destination to deliver change data capture events for a DynamoDB table, the Kinesis Data Stream may need separate sizing management which will affect the overall costs. DynamoDB charges in terms of Change Data capture Units (CDUs) where each unit is a made of up a 1 KB DynamoDB item size attempted by the DynamoDB service to the destination Kinesis Data Stream.

In addition to charges by the DynamoDB service, standard Kinesis Data Stream charges will be incurred. As mentioned in the [pricing page](https://aws.amazon.com/kinesis/data-streams/pricing/), the service pricing differs based on the capacity mode - provisioned and on-demand, which are distinct from DynamoDB table capacity modes and are user-defined. At a high level, Kinesis Data Streams charges an hourly rate based on the capacity mode, as well as on data ingested into the stream by DynamoDB service. There may be additional charges like data retrieval (for on-demand mode), extended data retention (beyond default 24 hours), and enhanced fan-out consumer retrievals depending on the user configuration for the Kinesis Data Stream.

**Monitoring your Kinesis Data Streams usage**  
Kinesis Data Streams for DynamoDB publishes metrics from DynamoDB in addition to standard Kinesis Data Stream CloudWatch Metrics. It may be possible that a `Put` attempt by the DynamoDB service is throttled by the Kinesis service because of insufficient Kinesis Data Streams capacity, or by dependent components like a AWS KMS service that may be configured to encrypt the Kinesis Data Stream data at rest.

To learn more about CloudWatch metrics published by DynamoDB service for the Kinesis Data Stream, see [Monitoring change data capture with Kinesis Data Streams](kds_using-shards-and-metrics.md#kds_using-shards-and-metrics.monitoring). In order to avoid additional costs of service retries due to throttles, it is important to right size the Kinesis Data Stream in case of Provisioned Mode.

**Choosing the right capacity mode for Kinesis Data Streams**  
Kinesis Data Streams are supported in two capacity modes – provisioned mode and on-demand mode.
+ If the workload involving Kinesis Data Stream has predictable application traffic, traffic that is consistent or ramps gradually, or traffic that can be forecasted accurately, then Kinesis Data Streams’ **provisioned mode** is suitable and will be more cost efficient
+ If the workload is new, has unpredictable application traffic, or you prefer not to manage capacity, then Kinesis Data Streams’ **on-demand mode** is suitable and will be more cost efficient

A best practice to optimize costs would be to evaluate if the DynamoDB table associated with the Kinesis Data Stream has a predictable traffic pattern that can leverage provisioned mode of Kinesis Data Streams. If the workload is new, you could use on-demand mode for the Kinesis Data Streams for a few initial weeks, review the CloudWatch metrics to understand traffic patterns, and then switch the same Stream to provisioned mode based on the nature of the workload. In the case of provisioned mode, estimation on number shards can be made by following shard management considerations for Kinesis Data Streams.

**Evaluate your consumer applications using Kinesis Data Streams for DynamoDB**  
Since Kinesis Data Streams don’t charge on the number of `GetRecords` calls like DynamoDB Streams, consumer applications could make as many number of calls as possible, provided the frequency is under the throttling limits for `GetRecords`. In terms of on-demand mode for Kinesis Data Streams, data reads are charged on a per GB basis. For provisioned mode Kinesis Data Streams, reads are not charged if the data is less than 7 days old. In the case of Lambda functions as Kinesis Data Streams consumers, Lambda polls each shard in your Kinesis Stream for records at a base rate of once per second.

## Cost optimization strategies for both types of Streams usage
<a name="CostOptimization_StreamsUsage_GuidanceForBoth"></a>

**Event filtering for AWS Lambda consumers**  
Lambda event filtering allows you to discard events based on a filter criteria from being available in the Lambda function invocation batch. This optimizes Lambda costs for processing or discarding unwanted stream records within the consumer function logic. To learn more about configuring event filtering and writing your filtering criteria, see [Lambda event filtering](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html).

**Tuning AWS Lambda consumers**  
Costs could be further be optimized by tuning Lambda configuration parameters like increasing the `BatchSize` to process more per invocation, enabling `BisectBatchOnFunctionError` to prevent processing duplicates (which incurs additional costs), and setting `MaximumRetryAttempts` to not run into too many retries. By default, failed consumer Lambda invocations are retried infinitely until the record expires from the stream, which is around 24 hours for DynamoDB Streams and configurable from 24 hours to up to 1 year for Kinesis Data Streams. Additional Lambda configuration options available including the ones mentioned above for DynamoDB Stream consumers are in the [AWS Lambda developer guide](https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-params).

# Evaluate your provisioned capacity for right-sized provisioning in your DynamoDB table
<a name="CostOptimization_RightSizedProvisioning"></a>

This section provides an overview of how to evaluate if you have right-sized provisioning on your DynamoDB tables. As your workload evolves, you should modify your operational procedures appropriately, especially when your DynamoDB table is configured in provisioned mode and you have the risk to over-provision or under-provision your tables.

The procedures described below require statistical information that should be captured from the DynamoDB tables that are supporting your production application. To understand your application behavior, you should define a period of time that is significant enough to capture the data seasonality from your application. For example, if your application shows weekly patterns, using a three week period should give you enough room for analysing application throughput needs.

If you don’t know where to start, use at least one month’s worth of data usage for the calculations below.

While evaluating capacity, DynamoDB tables can configure **Read Capacity Units (RCUs)** and **Write Capacity Units (WCU)** independently. If your tables have any Global Secondary Indexes (GSI) configured, you will need to specify the throughput that it will consume, which will be also independent from the RCUs and WCUs from the base table.

**Note**  
Local Secondary Indexes (LSI) consume capacity from the base table.

**Topics**
+ [How to retrieve consumption metrics on your DynamoDB tables](#CostOptimization_RightSizedProvisioning_ConsumptionMetrics)
+ [How to identify under-provisioned DynamoDB tables](#CostOptimization_RightSizedProvisioning_UnderProvisionedTables)
+ [How to identify over-provisioned DynamoDB tables](#CostOptimization_RightSizedProvisioning_OverProvisionedTables)

## How to retrieve consumption metrics on your DynamoDB tables
<a name="CostOptimization_RightSizedProvisioning_ConsumptionMetrics"></a>

To evaluate the table and GSI capacity, monitor the following CloudWatch metrics and select the appropriate dimension to retrieve either table or GSI information:


| Read Capacity Units | Write Capacity Units | 
| --- | --- | 
|  `ConsumedReadCapacityUnits`  |  `ConsumedWriteCapacityUnits`  | 
|  `ProvisionedReadCapacityUnits`  |  `ProvisionedWriteCapacityUnits`  | 
|  `ReadThrottleEvents`  |  `WriteThrottleEvents`  | 

You can do this either through the AWS CLI or the AWS Management Console.

------
#### [ AWS CLI ]

Before we retrieve the table consumption metrics, we'll need to start by capturing some historical data points using the CloudWatch API.

Start by creating two files: `write-calc.json` and `read-calc.json`. These files will represent the calculations for a table or GSI. You'll need to update some of the fields, as indicated in the table below, to match your environment.


| Field Name | Definition | Example | 
| --- | --- | --- | 
| <table-name> | The name of the table that you will be analysing | SampleTable | 
| <period> | The period of time that you will be using to evaluate the utilization target, based in seconds | For a 1-hour period you should specify: 3600 | 
| <start-time> | The beginning of your evaluation interval, specified in ISO8601 format | 2022-02-21T23:00:00 | 
| <end-time> | The end of your evaluation interval, specified in ISO8601 format | 2022-02-22T06:00:00 | 

The write calculations file will retrieve the number of WCU provisioned and consumed in the time period for the date range specified. It will also generate a utilization percentage that will be used for analysis. The full content of the `write-calc.json` file should look like this:

```
{
  "MetricDataQueries": [
    {
      "Id": "provisionedWCU",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/DynamoDB",
          "MetricName": "ProvisionedWriteCapacityUnits",
          "Dimensions": [
            {
              "Name": "TableName",
              "Value": "<table-name>"
            }
          ]
        },
        "Period": <period>,
        "Stat": "Average"
      },
      "Label": "Provisioned",
      "ReturnData": false
    },
    {
      "Id": "consumedWCU",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/DynamoDB",
          "MetricName": "ConsumedWriteCapacityUnits",
          "Dimensions": [
            {
              "Name": "TableName",
              "Value": "<table-name>""
            }
          ]
        },
        "Period": <period>,
        "Stat": "Sum"
      },
      "Label": "",
      "ReturnData": false
    },
    {
      "Id": "m1",
      "Expression": "consumedWCU/PERIOD(consumedWCU)",
      "Label": "Consumed WCUs",
      "ReturnData": false
    },
    {
      "Id": "utilizationPercentage",
      "Expression": "100*(m1/provisionedWCU)",
      "Label": "Utilization Percentage",
      "ReturnData": true
    }
  ],
  "StartTime": "<start-time>",
  "EndTime": "<ent-time>",
  "ScanBy": "TimestampDescending",
  "MaxDatapoints": 24
}
```

The read calculations file uses a similar file. This file will retrieve how many RCUs were provisioned and consumed during the time period for the date range specified. The contents of the `read-calc.json` file should look like this:

```
{
  "MetricDataQueries": [
    {
      "Id": "provisionedRCU",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/DynamoDB",
          "MetricName": "ProvisionedReadCapacityUnits",
          "Dimensions": [
            {
              "Name": "TableName",
              "Value": "<table-name>"
            }
          ]
        },
        "Period": <period>,
        "Stat": "Average"
      },
      "Label": "Provisioned",
      "ReturnData": false
    },
    {
      "Id": "consumedRCU",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/DynamoDB",
          "MetricName": "ConsumedReadCapacityUnits",
          "Dimensions": [
            {
              "Name": "TableName",
              "Value": "<table-name>"
            }
          ]
        },
        "Period": <period>,
        "Stat": "Sum"
      },
      "Label": "",
      "ReturnData": false
    },
    {
      "Id": "m1",
      "Expression": "consumedRCU/PERIOD(consumedRCU)",
      "Label": "Consumed RCUs",
      "ReturnData": false
    },
    {
      "Id": "utilizationPercentage",
      "Expression": "100*(m1/provisionedRCU)",
      "Label": "Utilization Percentage",
      "ReturnData": true
    }
  ],
  "StartTime": "<start-time>",
  "EndTime": "<end-time>",
  "ScanBy": "TimestampDescending",
  "MaxDatapoints": 24
}
```

One you've created the files, you can start retrieving utilization data.

1. To retreive the write utilization data, issue the following command:

   ```
   aws cloudwatch get-metric-data --cli-input-json file://write-calc.json
   ```

1. To retreive the read utilization data, issue the following command:

   ```
   aws cloudwatch get-metric-data --cli-input-json file://read-calc.json
   ```

The result for both queries will be a series of data points in JSON format that will be used for analysis. Your result will depend on the number of data points you specified, the period, and your own specific workload data. It could look something like this:

```
{
    "MetricDataResults": [
        {
            "Id": "utilizationPercentage",
            "Label": "Utilization Percentage",
            "Timestamps": [
                "2022-02-22T05:00:00+00:00",
                "2022-02-22T04:00:00+00:00",
                "2022-02-22T03:00:00+00:00",
                "2022-02-22T02:00:00+00:00",
                "2022-02-22T01:00:00+00:00",
                "2022-02-22T00:00:00+00:00",
                "2022-02-21T23:00:00+00:00"
            ],
            "Values": [
                91.55364583333333,
                55.066631944444445,
                2.6114930555555556,
                24.9496875,
                40.94725694444445,
                25.61819444444444,
                0.0
            ],
            "StatusCode": "Complete"
        }
    ],
    "Messages": []
}
```

**Note**  
If you specify a short period and a long time range, you might need to modify the `MaxDatapoints` which is by default set to 24 in the script. This represents one data point per hour and 24 per day.

------
#### [ AWS Management Console ]

1. Log into the AWS Management Console and navigate to the CloudWatch service page. Select an appropriate AWS Region if necessary.

1. Locate the **Metrics** section on the left navigation bar and select **All metrics**.

1. This will open a dashboard with two panels. The top panel will show you the graphic, and the bottom panel will show the metrics you want to graph. Choose **DynamoDB**.

1. Choose **Table Metrics**. This will show you the tables in your current Region.

1. Use the Search box to search for your table name and choose the write operation metrics: `ConsumedWriteCapacityUnits` and `ProvisionedWriteCapacityUnits`
**Note**  
This example talks about write operation metrics, but you can also use these steps to graph the read operation metrics.

1. Choose the **Graphed metrics (2)** tab to modify the formulas. By default, CloudWatch selects the statistical function **Average** for the graphs.  
![\[The selected graphed metrics and Average as the default statistical function.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning1.png)

1. While having both graphed metrics selected (the checkbox on the left) select the menu **Add math**, followed by **Common**, and then select the **Percentage** function. Repeat the procedure twice.

   First time selecting the **Percentage** function:  
![\[CloudWatch console. The Percentage function is selected for the graphed metrics.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning2.png)

   Second time selecting the **Percentage** function:  
![\[CloudWatch console. The Percentage function is selected a second time for the graphed metrics.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning3.png)

1. At this point you should have four metrics in the bottom menu. Let’s work on the `ConsumedWriteCapacityUnits` calculation. To be consistent, we need to match the names for the ones we used in the AWS CLI section. Click on the **m1 ID** and change this value to **consumedWCU**.   
![\[CloudWatch console. The graphed metric with m1 ID is renamed to consumedWCU.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning4.png)

   Rename the **ConsumedWriteCapacityUnit** label as **consumedWCU**.  
![\[The graphed metric with ConsumedWriteCapacityUnit label is renamed to consumedWCU.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning5.png)

1. Change the statistic from **Average** to **Sum**. This action will automatically create another metric called **ANOMALY\$1DETECTION\$1BAND**. For the scope of this procedure, let's ignore it by removing the checkbox on the newly generated **ad1 metric**.  
![\[CloudWatch console. The statistic SUM is selected in the dropdown list for the graphed metrics.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning6.png)  
![\[CloudWatch console. The ANOMALY_DETECTION_BAND metric is removed from the list of graphed metrics.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning7.png)

1. Repeat step 8 to rename the **m2 ID** to **provisionedWCU**. Leave the statistic set to **Average**.  
![\[CloudWatch console. The graphed metric with m2 ID is renamed to provisionedWCU.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning8.png)

1. Select the **Expression1** label and update the value to **m1** and the label to **Consumed WCUs**.
**Note**  
Make sure you have only selected **m1** (checkbox on the left) and **provisionedWCU** to properly visualize the data. Update the formula by clicking in **Details** and changing the formula to **consumedWCU/PERIOD(consumedWCU)**. This step might also generate another **ANOMALY\$1DETECTION\$1BAND** metric, but for the scope of this procedure we can ignore it.  

![\[m1 and provisionedWCU are selected. Details for m1 is updated as consumedWCU/PERIOD(consumedWCU).\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning10.png)


1. You should have now have two graphics: one that indicates your provisioned WCUs on the table and another that indicates the consumed WCUs. The shape of the graphic might be different from the one below, but you can use it as reference:  
![\[Graph with the provisioned WCUs and consumed WCUs for the table plotted.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning11.png)

1. Update the percentage formula by selecting the Expression2 graphic (**e2**). Rename the labels and IDs to **utilizationPercentage**. Rename the formula to match **100\$1(m1/provisionedWCU)**.  
![\[CloudWatch console. Labels and IDs for Expression2 are renamed to utilizationPercentage.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning12.png)  
![\[CloudWatch console. Percentage formula for Expression2 is updated to 100*(m1/provisionedWCU).\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning13.png)

1. Remove the checkbox from all the metrics but **utilizationPercentage** to visualize your utilization patterns. The default interval is set to 1 minute, but feel free to modify it as you need.  
![\[Graph of the utilizationPercentage metric for the selected time interval.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning14.png)

Here is view of a longer period of time as well as a bigger period of 1 hour. You can see there are some intervals where the utilization was higher than 100%, but this particular workload has longer intervals with zero utilization.

![\[Utilization pattern for an extended period. It highlights periods of utilization over 100% and zero.\]](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning15.png)


At this point, you might have different results from the pictures in this example. It all depends on the data from your workload. Intervals with more than 100% utilization are prone to throttling events. DynamoDB offers [burst capacity](burst-adaptive-capacity.md#burst-capacity), but as soon as the burst capacity is done anything above 100% will be throttled.

------

## How to identify under-provisioned DynamoDB tables
<a name="CostOptimization_RightSizedProvisioning_UnderProvisionedTables"></a>

For most workloads, a table is considered under-provisioned when it constantly consumes more than 80% of their provisioned capacity.

[Burst capacity](burst-adaptive-capacity.md#burst-capacity) is a DynamoDB feature that allow customers to temporarily consume more RCUs/WCUs than originally provisioned (more than the per-second provisioned throughput that was defined in the table). The burst capacity was created to absorb sudden increases in traffic due to special events or usage spikes. This burst capacity doesn’t last forever. As soon as the unused RCUs and WCUs are depleted, you will get throttled if you try to consume more capacity than provisioned. When your application traffic is getting close to the 80% utilization rate, your risk of throttling is significantly higher.

The 80% utilization rate rule varies from the seasonality of your data and your traffic growth. Consider the following scenarios: 
+ If your traffic has been **stable** at \$190% utilization rate for the last 12 months, your table has just the right capacity
+ If your application traffic is **growing** at a rate of 8% monthly in less than 3 months, you will arrive at 100%
+ If your application traffic is **growing** at a rate of 5% in a little more than 4 months, you will still arrive at 100%

The results from the queries above provide a picture of your utilization rate. Use them as a guide to further evaluate other metrics that can help you choose to increase your table capacity as required (for example: a monthly or weekly growth rate). Work with your operations team to define what is a good percentage for your workload and your tables.

There are special scenarios where the data is skewed when we analyse it on a daily or weekly basis. For example, with seasonal applications that have spikes in usage during working hours (but then drops to almost zero outside of working hours), you could benefit by [scheduling auto scaling](https://docs.aws.amazon.com/autoscaling/application/userguide/examples-scheduled-actions.html) where you specify the hours of the day (and the days of the week) to increase the provisioned capacity and when to reduce it. Instead of aiming for higher capacity so you can cover the busy hours, you can also benefit from [DynamoDB table auto scaling](AutoScaling.md) configurations if your seasonality is less pronounced.

**Note**  
When you create a DynamoDB auto scaling configuration for your base table, remember to include another configuration for any GSI that is associated with the table.

## How to identify over-provisioned DynamoDB tables
<a name="CostOptimization_RightSizedProvisioning_OverProvisionedTables"></a>

The query results obtained from the scripts above provide the data points required to perform some initial analysis. If your data set presents values lower than 20% utilization for several intervals, your table might be over-provisioned. To further define if you need to reduce the number of WCUs and RCUS, you should revisit the other readings in the intervals.

When your tables contain several low usage intervals, you can really benefit from using auto scaling policies, either by scheduling auto scaling or just configuring the default auto scaling policies for the table that are based on utilization.

If you have a workload with low utilization to high throttle ratio (**Max(ThrottleEvents)/Min(ThrottleEvents) **in the interval), this could happen when you have a very spiky workload where traffic increases a lot during some days (or hours), but in general the traffic is consistently low. In these scenarios it might be beneficial to use [scheduled auto scaling](https://docs.aws.amazon.com/autoscaling/application/userguide/examples-scheduled-actions.html).

The AWS [Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/) helps cloud architects build secure, high-performing, resilient, and efficient infrastructure for a variety of applications and workloads. Built around six pillars—operational excellence, security, reliability, performance efficiency, cost optimization, and sustainability—AWS Well-Architected provides a consistent approach for customers and partners to evaluate architectures and implement scalable designs.

The AWS [Well-Architected Lenses](https://docs.aws.amazon.com/wellarchitected/latest/userguide/lenses.html) extend the guidance offered by AWS Well-Architected to specific industry and technology domains. The Amazon DynamoDB Well-Architected Lens focuses on DynamoDB workloads. It provides best practices, design principles and questions to assess and review a DynamoDB workload. Completing an Amazon DynamoDB Well-Architected Lens review will provide you with education and guidance around recommended design principles as it relates to each of the AWS Well-Architected pillars. This guidance is based on our experience working with customers across various industries, segments, sizes and geographies.

 As a direct outcome of the Well-Architected Lens review, you will receive a summary of actionable recommendations to optimize and improve your DynamoDB workload. 

## Conducting the Amazon DynamoDB Well-Architected Lens review
<a name="bp-wal-conducting"></a>

The DynamoDB Well-Architected Lens review is usually performed by an AWS Solutions Architect together with the customer, but can also be performed by the customer as a self-service. While we recommend reviewing all six of the Well-Architected Pillars as part of the Amazon DynamoDB Well-Architected Lens, you can also decide to prioritize your focus on one or more pillars first.

Additional information and instructions for conducting an Amazon DynamoDB Well-Architected Lens review are available in [this video ](https://youtu.be/mLAUvJYvBjA) and the [DynamoDB Well-Architected Lens GitHub page ](https://github.com/aws-samples/custom-lens-wa-hub/tree/main/DynamoDB).

## The pillars of the Amazon DynamoDB Well-Architected Lens
<a name="bp-wal-pillars"></a>

The Amazon DynamoDB Well-Architected Lens is built around six pillars:

**Performance efficiency pillar**

The performance efficiency pillar includes the ability to use computing resources efficiently to meet system requirements, and to maintain that efficiency as demand changes and technologies evolve.

The primary DynamoDB design principles for this pillar revolve around [modeling the data ](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-relational-modeling.html), [choosing partition keys ](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html#HowItWorks.Partitions.SimpleKey) and [sort keys ](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html#HowItWorks.Partitions.CompositeKey), and [defining secondary indexes ](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-indexes.html) based on the application access patterns. Additional considerations include choosing the optimal throughput mode for the workload, AWS SDK tuning and, when appropriate, using an optimal caching strategy. To learn more about these design principles, watch this [deep dive video ](https://youtu.be/PuCIy5Weyi8) about the performance efficiency pillar of the DynamoDB Well-Architected Lens.

**Cost optimization pillar**

The cost optimization pillar focuses on avoiding unnecessary costs. 

Key topics include understanding and controlling where money is being spent, selecting the most appropriate and right number of resource types, analyzing spend over time, designing your data models to optimize the cost for application-specific access patterns, and scaling to meet business needs without overspending.

The key cost optimization design principles for DynamoDB revolve around choosing the most appropriate capacity mode and table class for your tables and avoiding over-provisioning capacity by either using the on-demand capacity mode, or provisioned capacity mode with autoscaling. Additional considerations include efficient data modeling and querying to reduce the amount of consumed capacity, reserving portions of the consumed capacity at discounted price, minimizing item size, identifying and removing unused resources and using [TTL](TTL.md) to automatically delete aged-out data at no cost. To learn more about these design principles, watch this [deep dive video](https://youtu.be/iuI0HUuw6Jg) about the cost optimization pillar of the DynamoDB Well-Architected Lens.

See [Cost optimization](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-cost-optimization.html) for additional information on cost optimization best practices for DynamoDB.

**Operational excellence pillar**

The operational excellence pillar focuses on running and monitoring systems to deliver business value, and continually improving processes and procedures. Key topics include automating changes, responding to events, and defining standards to manage daily operations.

The main operational excellence design principles for DynamoDB include monitoring DynamoDB metrics through Amazon CloudWatch and AWS Config and automatically alert and remediate when predefined thresholds are breached, or non compliant rules are detected. Additional considerations are defining DynamoDB resources via infrastructure as a code and leveraging tags for better organization, identification and cost accounting of your DynamoDB resources. To learn more about these design principles, watch this [deep dive video ](https://youtu.be/41HUSL9tJa8) about the operational excellence pillar of the DynamoDB Well-Architected Lens.

**Reliability pillar**

The reliability pillar focuses on ensuring a workload performs its intended function correctly and consistently when it’s expected to. A resilient workload quickly recovers from failures to meet business and customer demand. Key topics include distributed system design, recovery planning, and how to handle change.

The essential reliability design principles for DynamoDB revolve around choosing the backup strategy and retention based on your RPO and RTO requirements, using DynamoDB global tables for multi-regional workloads, or cross-region disaster recovery scenarios with low RTO, implementing retry logic with exponential backoff in the application by configuring and using these capabilities in the AWS SDK, and monitoring DynamoDB metrics through Amazon CloudWatch and automatically alerting and remediating when predefined thresholds are breached. To learn more about these design principles, watch this [deep dive video ](https://youtu.be/8AoPBxVQYM8) about the reliability pillar of the DynamoDB Well-Architected Lens.

**Security pillar**

The security pillar focuses on protecting information and systems. Key topics include confidentiality and integrity of data, identifying and managing who can do what with privilege management, protecting systems, and establishing controls to detect security events.

The main security design principles for DynamoDB are encrypting data in transit with HTTPS, choosing the type of keys for data at rest encryption and defining the IAM roles and policies to authenticate, authorize and provide fine grain access to DynamoDB resources. Additional considerations include auditing DynamoDB control plane and data plane operations through AWS CloudTrail. To learn more about these design principles, watch this [deep dive video](https://youtu.be/95prjv2EEXA?si=xvNci2MM856siejv) about the security pillar of the DynamoDB Well-Architected Lens.

See [Security](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/security.html) for additional information on security for DynamoDB.

**Sustainability pillar**

The sustainability pillar focuses on minimizing the environmental impacts of running cloud workloads. Key topics include a shared responsibility model for sustainability, understanding impact, and maximizing utilization to minimize required resources and reduce downstream impacts.

The main sustainability design principles for DynamoDB include identifying and removing unused DynamoDB resources, avoiding over-provisioning though the usage of on-demand capacity mode or provisioned capacity-mode with autoscaling, efficient querying to reduce the amount of capacity being consumed and reduction of the storage footprint by compressing data and by deleting aged-out data through the use of TTL. To learn more about these design principles, watch this [deep dive video ](https://youtu.be/fAfYms7u3EE) about the sustainability pillar of the DynamoDB Well-Architected Lens.