AWS DynamoDB Complete Guide: Architecture, Indexing & Performance Optimization

Introduction

Amazon DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability. This comprehensive guide explores DynamoDB’s architecture, data structures, indexing strategies, and advanced optimization techniques to achieve maximum performance for your applications.

DynamoDB Architecture Overview

Core Architecture Components

graph TB
    subgraph "DynamoDB Service Architecture"
        APP[Application Layer]
        SDK[AWS SDK]
        API[DynamoDB API]

        subgraph "DynamoDB Core"
            AUTH[Authentication & Authorization]
            ROUTER[Request Router]
            METADATA[Metadata Service]

            subgraph "Storage Layer"
                PARTITION1[Partition 1]
                PARTITION2[Partition 2]
                PARTITION3[Partition 3]
                PARTITIONN[Partition N]
            end

            subgraph "Index Layer"
                GSI[Global Secondary Indexes]
                LSI[Local Secondary Indexes]
            end
        end

        subgraph "Infrastructure"
            SSD[SSD Storage]
            REPLICATION[Multi-AZ Replication]
            BACKUP[Automated Backups]
        end
    end

    APP --> SDK
    SDK --> API
    API --> AUTH
    AUTH --> ROUTER
    ROUTER --> METADATA
    ROUTER --> PARTITION1
    ROUTER --> PARTITION2
    ROUTER --> PARTITION3
    ROUTER --> PARTITIONN

    PARTITION1 --> SSD
    PARTITION2 --> SSD
    PARTITION3 --> SSD
    PARTITIONN --> SSD

    SSD --> REPLICATION
    REPLICATION --> BACKUP

DynamoDB vs Traditional Databases

FeatureDynamoDBTraditional RDBMSMongoDB
Data ModelKey-Value & DocumentRelational TablesDocument
SchemaSchema-lessFixed SchemaFlexible Schema
ScalingHorizontal (Auto)Vertical (Manual)Horizontal (Manual)
ConsistencyEventually ConsistentACID TransactionsConfigurable
Query LanguagePartiQL & APIsSQLMongoDB Query Language
PerformanceSingle-digit millisecondVariableVariable
ManagementFully ManagedSelf-ManagedSelf/Managed Options

DynamoDB Data Structures

Primary Key Structures

DynamoDB supports two types of primary keys:

  1. Partition Key (Simple Primary Key)
  2. Composite Primary Key (Partition Key + Sort Key)
  1@Service
  2public class DynamoDBDataStructureDemo {
  3
  4    private final DynamoDbClient dynamoDbClient;
  5    private final DynamoDbEnhancedClient enhancedClient;
  6
  7    public DynamoDBDataStructureDemo() {
  8        this.dynamoDbClient = DynamoDbClient.builder()
  9            .region(Region.US_EAST_1)
 10            .credentialsProvider(DefaultCredentialsProvider.create())
 11            .build();
 12
 13        this.enhancedClient = DynamoDbEnhancedClient.builder()
 14            .dynamoDbClient(dynamoDbClient)
 15            .build();
 16    }
 17
 18    // Example 1: Simple Primary Key (Partition Key Only)
 19    @DynamoDbBean
 20    public static class UserProfile {
 21        private String userId;        // Partition Key
 22        private String username;
 23        private String email;
 24        private Instant createdAt;
 25        private Map<String, String> metadata;
 26
 27        @DynamoDbPartitionKey
 28        public String getUserId() { return userId; }
 29        public void setUserId(String userId) { this.userId = userId; }
 30
 31        public String getUsername() { return username; }
 32        public void setUsername(String username) { this.username = username; }
 33
 34        public String getEmail() { return email; }
 35        public void setEmail(String email) { this.email = email; }
 36
 37        @DynamoDbAttribute("created_at")
 38        public Instant getCreatedAt() { return createdAt; }
 39        public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
 40
 41        public Map<String, String> getMetadata() { return metadata; }
 42        public void setMetadata(Map<String, String> metadata) { this.metadata = metadata; }
 43    }
 44
 45    // Example 2: Composite Primary Key (Partition Key + Sort Key)
 46    @DynamoDbBean
 47    public static class OrderItem {
 48        private String orderId;       // Partition Key
 49        private String itemId;        // Sort Key
 50        private String productName;
 51        private Integer quantity;
 52        private BigDecimal price;
 53        private Instant timestamp;
 54
 55        @DynamoDbPartitionKey
 56        public String getOrderId() { return orderId; }
 57        public void setOrderId(String orderId) { this.orderId = orderId; }
 58
 59        @DynamoDbSortKey
 60        public String getItemId() { return itemId; }
 61        public void setItemId(String itemId) { this.itemId = itemId; }
 62
 63        public String getProductName() { return productName; }
 64        public void setProductName(String productName) { this.productName = productName; }
 65
 66        public Integer getQuantity() { return quantity; }
 67        public void setQuantity(Integer quantity) { this.quantity = quantity; }
 68
 69        public BigDecimal getPrice() { return price; }
 70        public void setPrice(BigDecimal price) { this.price = price; }
 71
 72        public Instant getTimestamp() { return timestamp; }
 73        public void setTimestamp(Instant timestamp) { this.timestamp = timestamp; }
 74    }
 75
 76    public void demonstrateDataStructures() {
 77        // Working with Simple Primary Key
 78        DynamoDbTable<UserProfile> userTable = enhancedClient.table("UserProfiles",
 79                                                    TableSchema.fromBean(UserProfile.class));
 80
 81        UserProfile user = new UserProfile();
 82        user.setUserId("user-12345");
 83        user.setUsername("johndoe");
 84        user.setEmail("john@example.com");
 85        user.setCreatedAt(Instant.now());
 86        user.setMetadata(Map.of("department", "engineering", "level", "senior"));
 87
 88        userTable.putItem(user);
 89
 90        // Working with Composite Primary Key
 91        DynamoDbTable<OrderItem> orderTable = enhancedClient.table("OrderItems",
 92                                                 TableSchema.fromBean(OrderItem.class));
 93
 94        OrderItem orderItem = new OrderItem();
 95        orderItem.setOrderId("order-67890");
 96        orderItem.setItemId("item-001");
 97        orderItem.setProductName("Laptop");
 98        orderItem.setQuantity(1);
 99        orderItem.setPrice(new BigDecimal("999.99"));
100        orderItem.setTimestamp(Instant.now());
101
102        orderTable.putItem(orderItem);
103    }
104}

DynamoDB Data Types

 1@Component
 2public class DynamoDBDataTypesDemo {
 3
 4    // Scalar Types
 5    @DynamoDbBean
 6    public static class ScalarTypeExample {
 7        private String stringValue;      // String (S)
 8        private Integer numberValue;     // Number (N)
 9        private byte[] binaryValue;      // Binary (B)
10        private Boolean booleanValue;    // Boolean (BOOL)
11        private String nullValue;        // Null (NULL)
12
13        // Getters and Setters...
14    }
15
16    // Document Types
17    @DynamoDbBean
18    public static class DocumentTypeExample {
19        private List<String> stringList;           // List (L)
20        private Map<String, String> stringMap;     // Map (M)
21        private Set<String> stringSet;             // String Set (SS)
22        private Set<Integer> numberSet;            // Number Set (NS)
23        private Set<byte[]> binarySet;             // Binary Set (BS)
24
25        // Getters and Setters...
26    }
27
28    // Complex Nested Structure
29    @DynamoDbBean
30    public static class ProductCatalog {
31        private String productId;
32        private String title;
33        private String description;
34        private BigDecimal price;
35        private List<String> categories;
36        private Map<String, Object> attributes;
37        private ProductDetails details;
38        private List<Review> reviews;
39
40        @DynamoDbPartitionKey
41        public String getProductId() { return productId; }
42        public void setProductId(String productId) { this.productId = productId; }
43
44        // Nested object as Map
45        public static class ProductDetails {
46            private String brand;
47            private String model;
48            private Map<String, String> specifications;
49            private List<String> images;
50
51            // Getters and Setters...
52        }
53
54        public static class Review {
55            private String reviewId;
56            private String userId;
57            private Integer rating;
58            private String comment;
59            private Instant reviewDate;
60
61            // Getters and Setters...
62        }
63
64        // Getters and Setters for all fields...
65    }
66}

Indexing Strategies

Global Secondary Indexes (GSI)

  1@Service
  2public class GlobalSecondaryIndexService {
  3
  4    private final DynamoDbClient dynamoDbClient;
  5
  6    public void createTableWithGSI() {
  7        // Create table with GSI
  8        CreateTableRequest createTableRequest = CreateTableRequest.builder()
  9            .tableName("GameScores")
 10            .keySchema(
 11                KeySchemaElement.builder()
 12                    .attributeName("UserId")
 13                    .keyType(KeyType.HASH)
 14                    .build(),
 15                KeySchemaElement.builder()
 16                    .attributeName("GameTitle")
 17                    .keyType(KeyType.RANGE)
 18                    .build()
 19            )
 20            .attributeDefinitions(
 21                AttributeDefinition.builder()
 22                    .attributeName("UserId")
 23                    .attributeType(ScalarAttributeType.S)
 24                    .build(),
 25                AttributeDefinition.builder()
 26                    .attributeName("GameTitle")
 27                    .attributeType(ScalarAttributeType.S)
 28                    .build(),
 29                AttributeDefinition.builder()
 30                    .attributeName("TopScore")
 31                    .attributeType(ScalarAttributeType.N)
 32                    .build(),
 33                AttributeDefinition.builder()
 34                    .attributeName("TopScoreDateTime")
 35                    .attributeType(ScalarAttributeType.S)
 36                    .build()
 37            )
 38            .globalSecondaryIndexes(
 39                // GSI 1: Query by GameTitle and TopScore
 40                GlobalSecondaryIndex.builder()
 41                    .indexName("GameTitleIndex")
 42                    .keySchema(
 43                        KeySchemaElement.builder()
 44                            .attributeName("GameTitle")
 45                            .keyType(KeyType.HASH)
 46                            .build(),
 47                        KeySchemaElement.builder()
 48                            .attributeName("TopScore")
 49                            .keyType(KeyType.RANGE)
 50                            .build()
 51                    )
 52                    .projection(Projection.builder()
 53                        .projectionType(ProjectionType.ALL)
 54                        .build())
 55                    .provisionedThroughput(ProvisionedThroughput.builder()
 56                        .readCapacityUnits(5L)
 57                        .writeCapacityUnits(5L)
 58                        .build())
 59                    .build(),
 60
 61                // GSI 2: Sparse index for top players
 62                GlobalSecondaryIndex.builder()
 63                    .indexName("TopPlayerIndex")
 64                    .keySchema(
 65                        KeySchemaElement.builder()
 66                            .attributeName("TopScore")
 67                            .keyType(KeyType.HASH)
 68                            .build(),
 69                        KeySchemaElement.builder()
 70                            .attributeName("TopScoreDateTime")
 71                            .keyType(KeyType.RANGE)
 72                            .build()
 73                    )
 74                    .projection(Projection.builder()
 75                        .projectionType(ProjectionType.INCLUDE)
 76                        .nonKeyAttributes("UserId", "GameTitle")
 77                        .build())
 78                    .provisionedThroughput(ProvisionedThroughput.builder()
 79                        .readCapacityUnits(5L)
 80                        .writeCapacityUnits(5L)
 81                        .build())
 82                    .build()
 83            )
 84            .billingMode(BillingMode.PROVISIONED)
 85            .provisionedThroughput(ProvisionedThroughput.builder()
 86                .readCapacityUnits(5L)
 87                .writeCapacityUnits(5L)
 88                .build())
 89            .build();
 90
 91        dynamoDbClient.createTable(createTableRequest);
 92    }
 93
 94    // Query using GSI
 95    public List<Map<String, AttributeValue>> queryByGameTitle(String gameTitle) {
 96        QueryRequest queryRequest = QueryRequest.builder()
 97            .tableName("GameScores")
 98            .indexName("GameTitleIndex")
 99            .keyConditionExpression("GameTitle = :gameTitle")
100            .expressionAttributeValues(Map.of(
101                ":gameTitle", AttributeValue.builder().s(gameTitle).build()
102            ))
103            .scanIndexForward(false) // Descending order by TopScore
104            .limit(10)
105            .build();
106
107        QueryResponse response = dynamoDbClient.query(queryRequest);
108        return response.items();
109    }
110
111    // Query top scores across all games
112    public List<Map<String, AttributeValue>> queryTopScores(int minScore) {
113        QueryRequest queryRequest = QueryRequest.builder()
114            .tableName("GameScores")
115            .indexName("TopPlayerIndex")
116            .keyConditionExpression("TopScore >= :minScore")
117            .expressionAttributeValues(Map.of(
118                ":minScore", AttributeValue.builder().n(String.valueOf(minScore)).build()
119            ))
120            .scanIndexForward(false)
121            .limit(20)
122            .build();
123
124        QueryResponse response = dynamoDbClient.query(queryRequest);
125        return response.items();
126    }
127}

Local Secondary Indexes (LSI)

 1@Service
 2public class LocalSecondaryIndexService {
 3
 4    public void createTableWithLSI() {
 5        CreateTableRequest createTableRequest = CreateTableRequest.builder()
 6            .tableName("Music")
 7            .keySchema(
 8                KeySchemaElement.builder()
 9                    .attributeName("Artist")
10                    .keyType(KeyType.HASH)
11                    .build(),
12                KeySchemaElement.builder()
13                    .attributeName("SongTitle")
14                    .keyType(KeyType.RANGE)
15                    .build()
16            )
17            .attributeDefinitions(
18                AttributeDefinition.builder()
19                    .attributeName("Artist")
20                    .attributeType(ScalarAttributeType.S)
21                    .build(),
22                AttributeDefinition.builder()
23                    .attributeName("SongTitle")
24                    .attributeType(ScalarAttributeType.S)
25                    .build(),
26                AttributeDefinition.builder()
27                    .attributeName("AlbumTitle")
28                    .attributeType(ScalarAttributeType.S)
29                    .build(),
30                AttributeDefinition.builder()
31                    .attributeName("Genre")
32                    .attributeType(ScalarAttributeType.S)
33                    .build()
34            )
35            .localSecondaryIndexes(
36                // LSI 1: Query songs by Artist and AlbumTitle
37                LocalSecondaryIndex.builder()
38                    .indexName("AlbumTitleIndex")
39                    .keySchema(
40                        KeySchemaElement.builder()
41                            .attributeName("Artist")
42                            .keyType(KeyType.HASH)
43                            .build(),
44                        KeySchemaElement.builder()
45                            .attributeName("AlbumTitle")
46                            .keyType(KeyType.RANGE)
47                            .build()
48                    )
49                    .projection(Projection.builder()
50                        .projectionType(ProjectionType.ALL)
51                        .build())
52                    .build(),
53
54                // LSI 2: Query songs by Artist and Genre
55                LocalSecondaryIndex.builder()
56                    .indexName("GenreIndex")
57                    .keySchema(
58                        KeySchemaElement.builder()
59                            .attributeName("Artist")
60                            .keyType(KeyType.HASH)
61                            .build(),
62                        KeySchemaElement.builder()
63                            .attributeName("Genre")
64                            .keyType(KeyType.RANGE)
65                            .build()
66                    )
67                    .projection(Projection.builder()
68                        .projectionType(ProjectionType.KEYS_ONLY)
69                        .build())
70                    .build()
71            )
72            .billingMode(BillingMode.PAY_PER_REQUEST)
73            .build();
74
75        dynamoDbClient.createTable(createTableRequest);
76    }
77
78    // Query using LSI
79    public List<Map<String, AttributeValue>> querySongsByAlbum(String artist, String albumTitle) {
80        QueryRequest queryRequest = QueryRequest.builder()
81            .tableName("Music")
82            .indexName("AlbumTitleIndex")
83            .keyConditionExpression("Artist = :artist AND AlbumTitle = :album")
84            .expressionAttributeValues(Map.of(
85                ":artist", AttributeValue.builder().s(artist).build(),
86                ":album", AttributeValue.builder().s(albumTitle).build()
87            ))
88            .build();
89
90        QueryResponse response = dynamoDbClient.query(queryRequest);
91        return response.items();
92    }
93}

Index Design Patterns

 1@Component
 2public class IndexDesignPatterns {
 3
 4    // Pattern 1: Inverted Index for Many-to-Many Relationships
 5    @DynamoDbBean
 6    public static class InvertedIndex {
 7        private String entityType;    // PK: "USER" or "SKILL"
 8        private String entityId;      // SK: actual ID
 9        private String relatedType;   // GSI PK: "SKILL" or "USER"
10        private String relatedId;     // GSI SK: related ID
11        private Map<String, String> metadata;
12
13        @DynamoDbPartitionKey
14        public String getEntityType() { return entityType; }
15        public void setEntityType(String entityType) { this.entityType = entityType; }
16
17        @DynamoDbSortKey
18        public String getEntityId() { return entityId; }
19        public void setEntityId(String entityId) { this.entityId = entityId; }
20
21        @DynamoDbSecondaryPartitionKey(indexNames = "InvertedIndex")
22        public String getRelatedType() { return relatedType; }
23        public void setRelatedType(String relatedType) { this.relatedType = relatedType; }
24
25        @DynamoDbSecondarySortKey(indexNames = "InvertedIndex")
26        public String getRelatedId() { return relatedId; }
27        public void setRelatedId(String relatedId) { this.relatedId = relatedId; }
28
29        public Map<String, String> getMetadata() { return metadata; }
30        public void setMetadata(Map<String, String> metadata) { this.metadata = metadata; }
31    }
32
33    // Pattern 2: Hierarchical Data with GSI
34    @DynamoDbBean
35    public static class HierarchicalData {
36        private String pk;            // Partition Key
37        private String sk;            // Sort Key
38        private String gsi1pk;        // GSI1 Partition Key
39        private String gsi1sk;        // GSI1 Sort Key
40        private String dataType;      // Entity type
41        private Map<String, Object> attributes;
42
43        @DynamoDbPartitionKey
44        public String getPk() { return pk; }
45        public void setPk(String pk) { this.pk = pk; }
46
47        @DynamoDbSortKey
48        public String getSk() { return sk; }
49        public void setSk(String sk) { this.sk = sk; }
50
51        @DynamoDbSecondaryPartitionKey(indexNames = "GSI1")
52        public String getGsi1pk() { return gsi1pk; }
53        public void setGsi1pk(String gsi1pk) { this.gsi1pk = gsi1pk; }
54
55        @DynamoDbSecondarySortKey(indexNames = "GSI1")
56        public String getGsi1sk() { return gsi1sk; }
57        public void setGsi1sk(String gsi1sk) { this.gsi1sk = gsi1sk; }
58
59        public String getDataType() { return dataType; }
60        public void setDataType(String dataType) { this.dataType = dataType; }
61
62        public Map<String, Object> getAttributes() { return attributes; }
63        public void setAttributes(Map<String, Object> attributes) { this.attributes = attributes; }
64    }
65
66    // Pattern 3: Time Series Data with Sort Key
67    @DynamoDbBean
68    public static class TimeSeriesData {
69        private String deviceId;      // Partition Key
70        private String timestamp;     // Sort Key (ISO 8601 format)
71        private String metricType;    // GSI Partition Key
72        private String timeGSI;       // GSI Sort Key (reversed timestamp)
73        private Double value;
74        private Map<String, String> tags;
75
76        @DynamoDbPartitionKey
77        public String getDeviceId() { return deviceId; }
78        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
79
80        @DynamoDbSortKey
81        public String getTimestamp() { return timestamp; }
82        public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
83
84        @DynamoDbSecondaryPartitionKey(indexNames = "MetricTypeIndex")
85        public String getMetricType() { return metricType; }
86        public void setMetricType(String metricType) { this.metricType = metricType; }
87
88        @DynamoDbSecondarySortKey(indexNames = "MetricTypeIndex")
89        public String getTimeGSI() { return timeGSI; }
90        public void setTimeGSI(String timeGSI) { this.timeGSI = timeGSI; }
91
92        public Double getValue() { return value; }
93        public void setValue(Double value) { this.value = value; }
94
95        public Map<String, String> getTags() { return tags; }
96        public void setTags(Map<String, String> tags) { this.tags = tags; }
97    }
98}

When to Use DynamoDB

Use Cases and Decision Matrix

ScenarioDynamoDBRDBMSDocument DB
High-scale web applicationsโœ… ExcellentโŒ Limited scalingโš ๏ธ Good with effort
Gaming leaderboardsโœ… Perfect for real-timeโŒ Slow updatesโš ๏ธ Moderate
IoT data ingestionโœ… Handles massive writesโŒ Bottlenecksโš ๏ธ Good
Session managementโœ… Fast key-value accessโŒ Overkillโœ… Also good
Complex reportingโŒ Limited analyticsโœ… Excellent SQLโš ๏ธ Aggregation framework
ACID transactionsโš ๏ธ Limited supportโœ… Full ACIDโš ๏ธ Limited
Ad-hoc queriesโŒ Requires known access patternsโœ… Flexible SQLโœ… Flexible
 1@Service
 2public class DynamoDBUseCaseExamples {
 3
 4    // Use Case 1: Gaming Leaderboard
 5    public void updatePlayerScore(String gameId, String playerId, int newScore) {
 6        UpdateItemRequest request = UpdateItemRequest.builder()
 7            .tableName("GameLeaderboard")
 8            .key(Map.of(
 9                "GameId", AttributeValue.builder().s(gameId).build(),
10                "PlayerId", AttributeValue.builder().s(playerId).build()
11            ))
12            .updateExpression("SET #score = if_not_exists(#score, :zero) + :increment, " +
13                             "#lastUpdated = :timestamp")
14            .conditionExpression("#score < :newScore OR attribute_not_exists(#score)")
15            .expressionAttributeNames(Map.of(
16                "#score", "HighScore",
17                "#lastUpdated", "LastUpdated"
18            ))
19            .expressionAttributeValues(Map.of(
20                ":zero", AttributeValue.builder().n("0").build(),
21                ":increment", AttributeValue.builder().n(String.valueOf(newScore)).build(),
22                ":newScore", AttributeValue.builder().n(String.valueOf(newScore)).build(),
23                ":timestamp", AttributeValue.builder().s(Instant.now().toString()).build()
24            ))
25            .returnValues(ReturnValue.ALL_NEW)
26            .build();
27
28        dynamoDbClient.updateItem(request);
29    }
30
31    // Use Case 2: IoT Sensor Data
32    public void batchWriteIoTData(List<SensorReading> readings) {
33        List<WriteRequest> writeRequests = readings.stream()
34            .map(reading -> WriteRequest.builder()
35                .putRequest(PutRequest.builder()
36                    .item(Map.of(
37                        "DeviceId", AttributeValue.builder().s(reading.getDeviceId()).build(),
38                        "Timestamp", AttributeValue.builder().s(reading.getTimestamp().toString()).build(),
39                        "Temperature", AttributeValue.builder().n(reading.getTemperature().toString()).build(),
40                        "Humidity", AttributeValue.builder().n(reading.getHumidity().toString()).build(),
41                        "Location", AttributeValue.builder().s(reading.getLocation()).build()
42                    ))
43                    .build())
44                .build())
45            .collect(Collectors.toList());
46
47        // Process in batches of 25 (DynamoDB limit)
48        Lists.partition(writeRequests, 25).forEach(batch -> {
49            BatchWriteItemRequest batchRequest = BatchWriteItemRequest.builder()
50                .requestItems(Map.of("IoTSensorData", batch))
51                .build();
52
53            dynamoDbClient.batchWriteItem(batchRequest);
54        });
55    }
56
57    // Use Case 3: Session Management
58    public void createUserSession(String sessionId, String userId, Duration ttl) {
59        long expirationTime = Instant.now().plus(ttl).getEpochSecond();
60
61        PutItemRequest request = PutItemRequest.builder()
62            .tableName("UserSessions")
63            .item(Map.of(
64                "SessionId", AttributeValue.builder().s(sessionId).build(),
65                "UserId", AttributeValue.builder().s(userId).build(),
66                "CreatedAt", AttributeValue.builder().s(Instant.now().toString()).build(),
67                "ExpiresAt", AttributeValue.builder().n(String.valueOf(expirationTime)).build(),
68                "TTL", AttributeValue.builder().n(String.valueOf(expirationTime)).build()
69            ))
70            .build();
71
72        dynamoDbClient.putItem(request);
73    }
74
75    public static class SensorReading {
76        private String deviceId;
77        private Instant timestamp;
78        private Double temperature;
79        private Double humidity;
80        private String location;
81
82        // Constructors, getters, and setters...
83    }
84}

Performance Optimization Strategies

Hot Partition Prevention

 1@Service
 2public class HotPartitionOptimizer {
 3
 4    // Strategy 1: Add Random Suffix to Distribute Load
 5    public String generateDistributedPartitionKey(String baseKey) {
 6        int suffix = ThreadLocalRandom.current().nextInt(0, 10);
 7        return baseKey + "#" + suffix;
 8    }
 9
10    // Strategy 2: Use Write Sharding for High-Volume Writes
11    public void writeWithSharding(String baseKey, Map<String, AttributeValue> item) {
12        String shardedKey = generateDistributedPartitionKey(baseKey);
13        item.put("PK", AttributeValue.builder().s(shardedKey).build());
14
15        PutItemRequest request = PutItemRequest.builder()
16            .tableName("HighVolumeTable")
17            .item(item)
18            .build();
19
20        dynamoDbClient.putItem(request);
21    }
22
23    // Strategy 3: Read from All Shards for Aggregated Data
24    public List<Map<String, AttributeValue>> readFromAllShards(String baseKey) {
25        List<CompletableFuture<QueryResponse>> futures = IntStream.range(0, 10)
26            .mapToObj(i -> {
27                String shardedKey = baseKey + "#" + i;
28                QueryRequest request = QueryRequest.builder()
29                    .tableName("HighVolumeTable")
30                    .keyConditionExpression("PK = :pk")
31                    .expressionAttributeValues(Map.of(
32                        ":pk", AttributeValue.builder().s(shardedKey).build()
33                    ))
34                    .build();
35
36                return CompletableFuture.supplyAsync(() -> dynamoDbClient.query(request));
37            })
38            .collect(Collectors.toList());
39
40        return futures.stream()
41            .map(CompletableFuture::join)
42            .flatMap(response -> response.items().stream())
43            .collect(Collectors.toList());
44    }
45
46    // Strategy 4: Adaptive Capacity Management
47    @Scheduled(fixedRate = 300000) // Every 5 minutes
48    public void monitorAndAdjustCapacity() {
49        DescribeTableRequest describeRequest = DescribeTableRequest.builder()
50            .tableName("HighVolumeTable")
51            .build();
52
53        DescribeTableResponse response = dynamoDbClient.describeTable(describeRequest);
54        TableDescription table = response.table();
55
56        // Check consumed capacity vs provisioned capacity
57        long readCapacity = table.provisionedThroughput().readCapacityUnits();
58        long writeCapacity = table.provisionedThroughput().writeCapacityUnits();
59
60        // Get metrics from CloudWatch (simplified example)
61        double readUtilization = getReadCapacityUtilization("HighVolumeTable");
62        double writeUtilization = getWriteCapacityUtilization("HighVolumeTable");
63
64        // Adjust capacity if utilization is high
65        if (readUtilization > 0.8 || writeUtilization > 0.8) {
66            updateTableCapacity(
67                "HighVolumeTable",
68                Math.round(readCapacity * 1.5f),
69                Math.round(writeCapacity * 1.5f)
70            );
71        }
72    }
73
74    private double getReadCapacityUtilization(String tableName) {
75        // Implementation would query CloudWatch metrics
76        return 0.0; // Placeholder
77    }
78
79    private double getWriteCapacityUtilization(String tableName) {
80        // Implementation would query CloudWatch metrics
81        return 0.0; // Placeholder
82    }
83
84    private void updateTableCapacity(String tableName, long readCapacity, long writeCapacity) {
85        ModifyTableRequest request = ModifyTableRequest.builder()
86            .tableName(tableName)
87            .provisionedThroughput(ProvisionedThroughput.builder()
88                .readCapacityUnits(readCapacity)
89                .writeCapacityUnits(writeCapacity)
90                .build())
91            .build();
92
93        dynamoDbClient.modifyTable(request);
94    }
95}

Fast I/O Optimization Techniques

  1@Service
  2public class FastIOOptimizer {
  3
  4    private final DynamoDbAsyncClient asyncClient;
  5    private final DynamoDbEnhancedAsyncClient enhancedAsyncClient;
  6
  7    public FastIOOptimizer() {
  8        this.asyncClient = DynamoDbAsyncClient.builder()
  9            .region(Region.US_EAST_1)
 10            .credentialsProvider(DefaultCredentialsProvider.create())
 11            // Configure HTTP client for better performance
 12            .httpClientBuilder(NettyNioAsyncHttpClient.builder()
 13                .maxConcurrency(100)
 14                .maxPendingConnectionAcquires(1000)
 15                .connectionTimeout(Duration.ofSeconds(2))
 16                .readTimeout(Duration.ofSeconds(30))
 17                .writeTimeout(Duration.ofSeconds(30))
 18            )
 19            .overrideConfiguration(ClientOverrideConfiguration.builder()
 20                .apiCallTimeout(Duration.ofSeconds(30))
 21                .apiCallAttemptTimeout(Duration.ofSeconds(10))
 22                .retryPolicy(RetryPolicy.builder()
 23                    .numRetries(3)
 24                    .build())
 25                .build())
 26            .build();
 27
 28        this.enhancedAsyncClient = DynamoDbEnhancedAsyncClient.builder()
 29            .dynamoDbClient(asyncClient)
 30            .build();
 31    }
 32
 33    // Technique 1: Batch Operations for Better Throughput
 34    public CompletableFuture<Void> batchWriteOptimized(List<Map<String, AttributeValue>> items) {
 35        // Split items into optimal batch size (25 items per batch)
 36        List<List<Map<String, AttributeValue>>> batches = Lists.partition(items, 25);
 37
 38        List<CompletableFuture<BatchWriteItemResponse>> batchFutures = batches.stream()
 39            .map(batch -> {
 40                List<WriteRequest> writeRequests = batch.stream()
 41                    .map(item -> WriteRequest.builder()
 42                        .putRequest(PutRequest.builder().item(item).build())
 43                        .build())
 44                    .collect(Collectors.toList());
 45
 46                BatchWriteItemRequest request = BatchWriteItemRequest.builder()
 47                    .requestItems(Map.of("FastTable", writeRequests))
 48                    .build();
 49
 50                return asyncClient.batchWriteItem(request);
 51            })
 52            .collect(Collectors.toList());
 53
 54        return CompletableFuture.allOf(batchFutures.toArray(new CompletableFuture[0]));
 55    }
 56
 57    // Technique 2: Parallel Query with Async
 58    public CompletableFuture<List<Map<String, AttributeValue>>> parallelQuery(
 59            List<String> partitionKeys) {
 60
 61        List<CompletableFuture<QueryResponse>> queryFutures = partitionKeys.stream()
 62            .map(pk -> {
 63                QueryRequest request = QueryRequest.builder()
 64                    .tableName("FastTable")
 65                    .keyConditionExpression("PK = :pk")
 66                    .expressionAttributeValues(Map.of(
 67                        ":pk", AttributeValue.builder().s(pk).build()
 68                    ))
 69                    .consistentRead(false) // Eventually consistent for better performance
 70                    .build();
 71
 72                return asyncClient.query(request);
 73            })
 74            .collect(Collectors.toList());
 75
 76        return CompletableFuture.allOf(queryFutures.toArray(new CompletableFuture[0]))
 77            .thenApply(v -> queryFutures.stream()
 78                .map(CompletableFuture::join)
 79                .flatMap(response -> response.items().stream())
 80                .collect(Collectors.toList()));
 81    }
 82
 83    // Technique 3: Connection Pooling and Reuse
 84    @Bean
 85    public DynamoDbClient optimizedDynamoDbClient() {
 86        return DynamoDbClient.builder()
 87            .region(Region.US_EAST_1)
 88            .httpClientBuilder(ApacheHttpClient.builder()
 89                .maxConnections(200)
 90                .connectionTimeout(Duration.ofSeconds(2))
 91                .socketTimeout(Duration.ofSeconds(30))
 92                .connectionTimeToLive(Duration.ofMinutes(5))
 93                .useIdleConnectionReaper(true)
 94            )
 95            .build();
 96    }
 97
 98    // Technique 4: Projection to Minimize Data Transfer
 99    public CompletableFuture<List<Map<String, AttributeValue>>> queryWithProjection(
100            String partitionKey) {
101
102        QueryRequest request = QueryRequest.builder()
103            .tableName("FastTable")
104            .keyConditionExpression("PK = :pk")
105            .projectionExpression("PK, SK, #name, #status, #timestamp")
106            .expressionAttributeNames(Map.of(
107                "#name", "Name",
108                "#status", "Status",
109                "#timestamp", "Timestamp"
110            ))
111            .expressionAttributeValues(Map.of(
112                ":pk", AttributeValue.builder().s(partitionKey).build()
113            ))
114            .build();
115
116        return asyncClient.query(request)
117            .thenApply(QueryResponse::items);
118    }
119
120    // Technique 5: Conditional Writes for Concurrency Control
121    public CompletableFuture<PutItemResponse> conditionalUpdateWithRetry(
122            String pk, String sk, Map<String, AttributeValue> updates, int maxRetries) {
123
124        return conditionalUpdateWithRetryHelper(pk, sk, updates, maxRetries, 0);
125    }
126
127    private CompletableFuture<PutItemResponse> conditionalUpdateWithRetryHelper(
128            String pk, String sk, Map<String, AttributeValue> updates, int maxRetries, int attempt) {
129
130        Map<String, AttributeValue> key = Map.of(
131            "PK", AttributeValue.builder().s(pk).build(),
132            "SK", AttributeValue.builder().s(sk).build()
133        );
134
135        // Add version for optimistic locking
136        updates.put("Version", AttributeValue.builder()
137            .n(String.valueOf(System.currentTimeMillis())).build());
138
139        PutItemRequest request = PutItemRequest.builder()
140            .tableName("FastTable")
141            .item(Stream.concat(key.entrySet().stream(), updates.entrySet().stream())
142                .collect(Collectors.toMap(
143                    Map.Entry::getKey,
144                    Map.Entry::getValue
145                )))
146            .conditionExpression("attribute_not_exists(PK) OR Version < :newVersion")
147            .expressionAttributeValues(Map.of(
148                ":newVersion", updates.get("Version")
149            ))
150            .build();
151
152        return asyncClient.putItem(request)
153            .exceptionallyCompose(throwable -> {
154                if (throwable.getCause() instanceof ConditionalCheckFailedException &&
155                    attempt < maxRetries) {
156
157                    // Exponential backoff
158                    return CompletableFuture.delayedExecutor(
159                            Duration.ofMillis(100 * (1L << attempt)),
160                            ForkJoinPool.commonPool())
161                        .thenCompose(v -> conditionalUpdateWithRetryHelper(
162                            pk, sk, updates, maxRetries, attempt + 1));
163                }
164                return CompletableFuture.failedFuture(throwable);
165            });
166    }
167}

DynamoDB Streams for Real-time Processing

  1@Service
  2public class DynamoDBStreamProcessor {
  3
  4    @EventListener
  5    public void handleDynamoDBStreamRecord(DynamodbEvent event) {
  6        List<CompletableFuture<Void>> processingFutures = event.getRecords().stream()
  7            .map(this::processRecord)
  8            .collect(Collectors.toList());
  9
 10        CompletableFuture.allOf(processingFutures.toArray(new CompletableFuture[0]))
 11            .join();
 12    }
 13
 14    private CompletableFuture<Void> processRecord(DynamodbEvent.DynamodbStreamRecord record) {
 15        return CompletableFuture.runAsync(() -> {
 16            String eventName = record.getEventName();
 17
 18            switch (eventName) {
 19                case "INSERT":
 20                    handleInsertEvent(record);
 21                    break;
 22                case "MODIFY":
 23                    handleModifyEvent(record);
 24                    break;
 25                case "REMOVE":
 26                    handleRemoveEvent(record);
 27                    break;
 28                default:
 29                    log.warn("Unknown event type: {}", eventName);
 30            }
 31        });
 32    }
 33
 34    private void handleInsertEvent(DynamodbEvent.DynamodbStreamRecord record) {
 35        Map<String, AttributeValue> newImage = record.getDynamodb().getNewImage();
 36
 37        // Example: Update search index
 38        updateSearchIndex(newImage, "ADD");
 39
 40        // Example: Send notification
 41        sendNotification("NEW_ITEM", newImage);
 42
 43        // Example: Update analytics
 44        updateAnalytics(newImage, "INSERT");
 45    }
 46
 47    private void handleModifyEvent(DynamodbEvent.DynamodbStreamRecord record) {
 48        Map<String, AttributeValue> oldImage = record.getDynamodb().getOldImage();
 49        Map<String, AttributeValue> newImage = record.getDynamodb().getNewImage();
 50
 51        // Compare and process changes
 52        Set<String> changedAttributes = findChangedAttributes(oldImage, newImage);
 53
 54        if (changedAttributes.contains("status")) {
 55            handleStatusChange(oldImage, newImage);
 56        }
 57
 58        if (changedAttributes.contains("priority")) {
 59            handlePriorityChange(oldImage, newImage);
 60        }
 61    }
 62
 63    private void handleRemoveEvent(DynamodbEvent.DynamodbStreamRecord record) {
 64        Map<String, AttributeValue> oldImage = record.getDynamodb().getOldImage();
 65
 66        // Clean up related resources
 67        cleanupRelatedData(oldImage);
 68
 69        // Update search index
 70        updateSearchIndex(oldImage, "DELETE");
 71
 72        // Update analytics
 73        updateAnalytics(oldImage, "DELETE");
 74    }
 75
 76    private Set<String> findChangedAttributes(Map<String, AttributeValue> oldImage,
 77                                            Map<String, AttributeValue> newImage) {
 78        Set<String> changedAttributes = new HashSet<>();
 79
 80        // Check all attributes in new image
 81        for (Map.Entry<String, AttributeValue> entry : newImage.entrySet()) {
 82            String attributeName = entry.getKey();
 83            AttributeValue newValue = entry.getValue();
 84            AttributeValue oldValue = oldImage.get(attributeName);
 85
 86            if (oldValue == null || !oldValue.equals(newValue)) {
 87                changedAttributes.add(attributeName);
 88            }
 89        }
 90
 91        // Check for removed attributes
 92        for (String attributeName : oldImage.keySet()) {
 93            if (!newImage.containsKey(attributeName)) {
 94                changedAttributes.add(attributeName);
 95            }
 96        }
 97
 98        return changedAttributes;
 99    }
100
101    // Placeholder methods for business logic
102    private void updateSearchIndex(Map<String, AttributeValue> item, String operation) {
103        log.info("Updating search index: {} - {}", operation, item.get("PK"));
104    }
105
106    private void sendNotification(String type, Map<String, AttributeValue> item) {
107        log.info("Sending notification: {} - {}", type, item.get("PK"));
108    }
109
110    private void updateAnalytics(Map<String, AttributeValue> item, String operation) {
111        log.info("Updating analytics: {} - {}", operation, item.get("PK"));
112    }
113
114    private void handleStatusChange(Map<String, AttributeValue> oldImage,
115                                   Map<String, AttributeValue> newImage) {
116        log.info("Status changed from {} to {}",
117                oldImage.get("status"), newImage.get("status"));
118    }
119
120    private void handlePriorityChange(Map<String, AttributeValue> oldImage,
121                                     Map<String, AttributeValue> newImage) {
122        log.info("Priority changed from {} to {}",
123                oldImage.get("priority"), newImage.get("priority"));
124    }
125
126    private void cleanupRelatedData(Map<String, AttributeValue> oldImage) {
127        log.info("Cleaning up related data for: {}", oldImage.get("PK"));
128    }
129}

Advanced Features

DynamoDB Transactions

  1@Service
  2public class DynamoDBTransactionService {
  3
  4    private final DynamoDbClient dynamoDbClient;
  5
  6    // ACID Transaction Example: Bank Transfer
  7    public void transferFunds(String fromAccountId, String toAccountId, BigDecimal amount) {
  8        List<TransactWriteItem> transactItems = Arrays.asList(
  9            // Debit from source account
 10            TransactWriteItem.builder()
 11                .update(Update.builder()
 12                    .tableName("BankAccounts")
 13                    .key(Map.of(
 14                        "AccountId", AttributeValue.builder().s(fromAccountId).build()
 15                    ))
 16                    .updateExpression("SET Balance = Balance - :amount, " +
 17                                    "LastUpdated = :timestamp, " +
 18                                    "#version = #version + :one")
 19                    .conditionExpression("Balance >= :amount AND " +
 20                                       "AccountStatus = :active")
 21                    .expressionAttributeNames(Map.of("#version", "Version"))
 22                    .expressionAttributeValues(Map.of(
 23                        ":amount", AttributeValue.builder().n(amount.toString()).build(),
 24                        ":timestamp", AttributeValue.builder().s(Instant.now().toString()).build(),
 25                        ":one", AttributeValue.builder().n("1").build(),
 26                        ":active", AttributeValue.builder().s("ACTIVE").build()
 27                    ))
 28                    .build())
 29                .build(),
 30
 31            // Credit to destination account
 32            TransactWriteItem.builder()
 33                .update(Update.builder()
 34                    .tableName("BankAccounts")
 35                    .key(Map.of(
 36                        "AccountId", AttributeValue.builder().s(toAccountId).build()
 37                    ))
 38                    .updateExpression("SET Balance = Balance + :amount, " +
 39                                    "LastUpdated = :timestamp, " +
 40                                    "#version = #version + :one")
 41                    .conditionExpression("AccountStatus = :active")
 42                    .expressionAttributeNames(Map.of("#version", "Version"))
 43                    .expressionAttributeValues(Map.of(
 44                        ":amount", AttributeValue.builder().n(amount.toString()).build(),
 45                        ":timestamp", AttributeValue.builder().s(Instant.now().toString()).build(),
 46                        ":one", AttributeValue.builder().n("1").build(),
 47                        ":active", AttributeValue.builder().s("ACTIVE").build()
 48                    ))
 49                    .build())
 50                .build(),
 51
 52            // Create transaction log
 53            TransactWriteItem.builder()
 54                .put(Put.builder()
 55                    .tableName("TransactionLog")
 56                    .item(Map.of(
 57                        "TransactionId", AttributeValue.builder()
 58                            .s(UUID.randomUUID().toString()).build(),
 59                        "FromAccount", AttributeValue.builder().s(fromAccountId).build(),
 60                        "ToAccount", AttributeValue.builder().s(toAccountId).build(),
 61                        "Amount", AttributeValue.builder().n(amount.toString()).build(),
 62                        "Timestamp", AttributeValue.builder()
 63                            .s(Instant.now().toString()).build(),
 64                        "Status", AttributeValue.builder().s("COMPLETED").build()
 65                    ))
 66                    .conditionExpression("attribute_not_exists(TransactionId)")
 67                    .build())
 68                .build()
 69        );
 70
 71        TransactWriteItemsRequest request = TransactWriteItemsRequest.builder()
 72            .transactItems(transactItems)
 73            .build();
 74
 75        try {
 76            dynamoDbClient.transactWriteItems(request);
 77            log.info("Transfer completed: {} -> {}, Amount: {}",
 78                    fromAccountId, toAccountId, amount);
 79        } catch (TransactionCanceledException e) {
 80            log.error("Transaction failed: {}", e.getMessage());
 81            handleTransactionFailure(e, fromAccountId, toAccountId, amount);
 82        }
 83    }
 84
 85    // Transaction Read Example
 86    public Map<String, Object> getAccountSummary(String accountId) {
 87        List<TransactGetItem> transactItems = Arrays.asList(
 88            TransactGetItem.builder()
 89                .get(Get.builder()
 90                    .tableName("BankAccounts")
 91                    .key(Map.of(
 92                        "AccountId", AttributeValue.builder().s(accountId).build()
 93                    ))
 94                    .build())
 95                .build(),
 96
 97            TransactGetItem.builder()
 98                .get(Get.builder()
 99                    .tableName("AccountPreferences")
100                    .key(Map.of(
101                        "AccountId", AttributeValue.builder().s(accountId).build()
102                    ))
103                    .build())
104                .build()
105        );
106
107        TransactGetItemsRequest request = TransactGetItemsRequest.builder()
108            .transactItems(transactItems)
109            .build();
110
111        TransactGetItemsResponse response = dynamoDbClient.transactGetItems(request);
112
113        Map<String, Object> summary = new HashMap<>();
114        summary.put("account", response.responses().get(0).item());
115        summary.put("preferences", response.responses().get(1).item());
116
117        return summary;
118    }
119
120    private void handleTransactionFailure(TransactionCanceledException e,
121                                        String fromAccountId, String toAccountId,
122                                        BigDecimal amount) {
123        // Log failure details
124        List<CancellationReason> reasons = e.cancellationReasons();
125        for (int i = 0; i < reasons.size(); i++) {
126            CancellationReason reason = reasons.get(i);
127            log.error("Transaction item {} failed: Code={}, Message={}",
128                     i, reason.code(), reason.message());
129        }
130
131        // Implement compensation logic if needed
132        createFailedTransactionRecord(fromAccountId, toAccountId, amount, e.getMessage());
133    }
134
135    private void createFailedTransactionRecord(String fromAccountId, String toAccountId,
136                                             BigDecimal amount, String failureReason) {
137        PutItemRequest request = PutItemRequest.builder()
138            .tableName("FailedTransactions")
139            .item(Map.of(
140                "TransactionId", AttributeValue.builder()
141                    .s(UUID.randomUUID().toString()).build(),
142                "FromAccount", AttributeValue.builder().s(fromAccountId).build(),
143                "ToAccount", AttributeValue.builder().s(toAccountId).build(),
144                "Amount", AttributeValue.builder().n(amount.toString()).build(),
145                "FailureReason", AttributeValue.builder().s(failureReason).build(),
146                "Timestamp", AttributeValue.builder().s(Instant.now().toString()).build()
147            ))
148            .build();
149
150        dynamoDbClient.putItem(request);
151    }
152}

Performance Monitoring and Optimization

CloudWatch Metrics and Alarms

  1@Component
  2public class DynamoDBMonitoring {
  3
  4    private final CloudWatchClient cloudWatchClient;
  5    private final DynamoDbClient dynamoDbClient;
  6
  7    public DynamoDBMonitoring(CloudWatchClient cloudWatchClient,
  8                             DynamoDbClient dynamoDbClient) {
  9        this.cloudWatchClient = cloudWatchClient;
 10        this.dynamoDbClient = dynamoDbClient;
 11    }
 12
 13    public void createPerformanceAlarms(String tableName) {
 14        // High read throttling alarm
 15        createAlarm(
 16            tableName + "-HighReadThrottles",
 17            "High read throttling detected",
 18            "AWS/DynamoDB",
 19            "UserErrors",
 20            Dimension.builder().name("TableName").value(tableName).build(),
 21            Statistic.SUM,
 22            300, // 5 minutes
 23            2,   // 2 evaluation periods
 24            10.0, // threshold
 25            ComparisonOperator.GREATER_THAN_THRESHOLD
 26        );
 27
 28        // High write throttling alarm
 29        createAlarm(
 30            tableName + "-HighWriteThrottles",
 31            "High write throttling detected",
 32            "AWS/DynamoDB",
 33            "SystemErrors",
 34            Dimension.builder().name("TableName").value(tableName).build(),
 35            Statistic.SUM,
 36            300,
 37            2,
 38            5.0,
 39            ComparisonOperator.GREATER_THAN_THRESHOLD
 40        );
 41
 42        // High consumed read capacity alarm
 43        createAlarm(
 44            tableName + "-HighReadCapacity",
 45            "High consumed read capacity",
 46            "AWS/DynamoDB",
 47            "ConsumedReadCapacityUnits",
 48            Dimension.builder().name("TableName").value(tableName).build(),
 49            Statistic.SUM,
 50            300,
 51            3,
 52            1000.0,
 53            ComparisonOperator.GREATER_THAN_THRESHOLD
 54        );
 55    }
 56
 57    private void createAlarm(String alarmName, String description, String namespace,
 58                           String metricName, Dimension dimension, Statistic statistic,
 59                           int period, int evaluationPeriods, double threshold,
 60                           ComparisonOperator comparisonOperator) {
 61
 62        PutMetricAlarmRequest request = PutMetricAlarmRequest.builder()
 63            .alarmName(alarmName)
 64            .alarmDescription(description)
 65            .comparisonOperator(comparisonOperator)
 66            .evaluationPeriods(evaluationPeriods)
 67            .metricName(metricName)
 68            .namespace(namespace)
 69            .period(period)
 70            .statistic(statistic)
 71            .threshold(threshold)
 72            .dimensions(dimension)
 73            .treatMissingData("notBreaching")
 74            .build();
 75
 76        cloudWatchClient.putMetricAlarm(request);
 77    }
 78
 79    @Scheduled(fixedRate = 300000) // Every 5 minutes
 80    public void collectCustomMetrics() {
 81        List<String> tableNames = getTableNames();
 82
 83        for (String tableName : tableNames) {
 84            try {
 85                collectTableMetrics(tableName);
 86            } catch (Exception e) {
 87                log.error("Failed to collect metrics for table: {}", tableName, e);
 88            }
 89        }
 90    }
 91
 92    private void collectTableMetrics(String tableName) {
 93        // Get table description for capacity information
 94        DescribeTableRequest request = DescribeTableRequest.builder()
 95            .tableName(tableName)
 96            .build();
 97
 98        DescribeTableResponse response = dynamoDbClient.describeTable(request);
 99        TableDescription table = response.table();
100
101        // Calculate capacity utilization
102        if (table.billingModeSummary() == null ||
103            table.billingModeSummary().billingMode() == BillingMode.PROVISIONED) {
104
105            calculateAndPublishCapacityUtilization(tableName, table);
106        }
107
108        // Publish custom business metrics
109        publishCustomMetrics(tableName);
110    }
111
112    private void calculateAndPublishCapacityUtilization(String tableName,
113                                                       TableDescription table) {
114        // This would typically involve querying CloudWatch for consumed capacity
115        // and calculating utilization percentages
116
117        double readUtilization = calculateReadUtilization(tableName);
118        double writeUtilization = calculateWriteUtilization(tableName);
119
120        // Publish custom metrics
121        PutMetricDataRequest request = PutMetricDataRequest.builder()
122            .namespace("Custom/DynamoDB")
123            .metricData(
124                MetricDatum.builder()
125                    .metricName("ReadCapacityUtilization")
126                    .value(readUtilization)
127                    .unit(StandardUnit.PERCENT)
128                    .dimensions(Dimension.builder()
129                        .name("TableName")
130                        .value(tableName)
131                        .build())
132                    .timestamp(Instant.now())
133                    .build(),
134
135                MetricDatum.builder()
136                    .metricName("WriteCapacityUtilization")
137                    .value(writeUtilization)
138                    .unit(StandardUnit.PERCENT)
139                    .dimensions(Dimension.builder()
140                        .name("TableName")
141                        .value(tableName)
142                        .build())
143                    .timestamp(Instant.now())
144                    .build()
145            )
146            .build();
147
148        cloudWatchClient.putMetricData(request);
149    }
150
151    private double calculateReadUtilization(String tableName) {
152        // Implementation would query CloudWatch for consumed vs provisioned capacity
153        return 0.0; // Placeholder
154    }
155
156    private double calculateWriteUtilization(String tableName) {
157        // Implementation would query CloudWatch for consumed vs provisioned capacity
158        return 0.0; // Placeholder
159    }
160
161    private void publishCustomMetrics(String tableName) {
162        // Example: Track record count, average item size, etc.
163        // This is application-specific logic
164    }
165
166    private List<String> getTableNames() {
167        ListTablesRequest request = ListTablesRequest.builder().build();
168        ListTablesResponse response = dynamoDbClient.listTables(request);
169        return response.tableNames();
170    }
171}

Conclusion

Amazon DynamoDB is a powerful NoSQL database service that excels in high-scale, low-latency applications. Key takeaways:

Architecture Benefits

  • Fully managed: No infrastructure management required
  • Auto-scaling: Handles traffic spikes automatically
  • High availability: Multi-AZ replication built-in
  • Consistent performance: Single-digit millisecond latency

Design Best Practices

  • Plan access patterns: Design tables around query requirements
  • Avoid hot partitions: Use techniques like write sharding
  • Optimize indexes: Use GSI and LSI strategically
  • Leverage batch operations: Maximize throughput efficiency

Performance Optimization

  • Use async clients: Better concurrency and resource utilization
  • Implement connection pooling: Reduce connection overhead
  • Monitor key metrics: Proactive capacity management
  • Consider DynamoDB Streams: Real-time data processing

When to Choose DynamoDB

  • High-scale web applications requiring fast, predictable performance
  • Gaming applications with real-time leaderboards and session management
  • IoT workloads with massive write throughput requirements
  • Mobile applications needing offline sync capabilities

Understanding these concepts and implementing the optimization techniques discussed will help you build high-performance applications that can scale to millions of users while maintaining consistent, fast response times.