- Table: UserTable
- Item:
- Partition Key: UserId
- Attribute:
- UserId: "123"
- Username: "JohnDoe"
- Address:
- Street: "123 Main St"
- City: "Cityville"
- ZipCode: "12345"
- Partition Key: UserId
- Attribute:
- UserId: "456"
- Username: "JaneSmith"
- Address:
- Street: "456 Oak St"
- City: "Towndale"
- ZipCode: "67890"
- Partition Key: UserId
- Attribute:
- UserId: "789"
- Username: "BobJohnson"
- Address:
- Street: "789 Pine St"
- City: "Villagetown"
- ZipCode: "54321"
NoSql 인 만큼 기본키 외에는 별도의 스키마가 존재하지 않습니다.
PK를 사용하여 데이터를 쿼리합니다.
테이블 생성시 고유 식별자를 가져야 하는데 이는 2종류로 나뉩니다.
DynamoDB 테이블에서 각 아이템을 식별하는 데 사용되는 주요 키입니다. 테이블을 여러 파티션으로 분할하고, 각 파티션은 고유한 파티션 키 값을 가지며 데이터를 저장합니다.
파티션 키와 함께 정렬 키(또는 범위 키)를 조합하여 사용하는 키입니다. 복합 키를 사용하면 파티션 내에서 데이터를 정렬하여 저장할 수 있습니다.
예시: "UserId"를 파티션 키로 하고 "Timestamp"를 정렬 키로 하는 경우, 특정 사용자의 아이템을 시간순으로 정렬하여 저장할 수 있습니다.
테이블: "UserTable"
파티션 키: UserId
예시 아이템:
UserId: "123", Username: "JohnDoe", Age: 25
테이블: "UserActivityTable"
파티션 키: UserId
정렬 키: Timestamp
예시 아이템:
UserId: "123", Timestamp: "2023-01-01T12:00:00", Action: "Login"
UserId: "123", Timestamp: "2023-01-02T09:30:00", Action: "Purchase"
UserId: "456", Timestamp: "2023-01-01T15:45:00", Action: "Logout"
보조 인덱스는 테이블의 기본 키 이외의 다른 어트리뷰트를 기반으로 데이터에 대한 추가적인 쿼리 및 정렬을 지원하는 인덱스입니다. DynamoDB는 기본 키 외에 보조 인덱스를 생성하여 여러 쿼리 패턴을 지원하고 성능을 최적화할 수 있게 합니다.
테이블의 기본 키와 같은 파티션 키를 공유하면서 정렬 키를 다르게 설정한 인덱스입니다.
파티션 키는 원래 테이블과 동일하며, 정렬 키만 다를 수 있습니다.
params = {
TableName: 'Books',
AttributeDefinitions: [
{ AttributeName: 'Author', AttributeType: 'S' }, // 테이블 기본 키의 파티션 키
{ AttributeName: 'Title', AttributeType: 'S' }, // 테이블 기본 키의 정렬 키
{ AttributeName: 'PublicationYear', AttributeType: 'N' }, // 로컬 보조 인덱스의 정렬 키
],
KeySchema: [
{ AttributeName: 'Author', KeyType: 'HASH' }, // 테이블 기본 키의 파티션 키
{ AttributeName: 'Title', KeyType: 'RANGE' }, // 테이블 기본 키의 정렬 키
],
LocalSecondaryIndexes: [
{
IndexName: 'PublicationYearIndex',
KeySchema: [
{ AttributeName: 'Author', KeyType: 'HASH' }, // 로컬 보조 인덱스의 파티션 키 (테이블 기본 키와 동일해야 함)
{ AttributeName: 'PublicationYear', KeyType: 'RANGE' }, // 로컬 보조 인덱스의 정렬 키
],
Projection: { ProjectionType: 'ALL' } // 모든 어트리뷰트를 포함
}
],
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5,
},
};
Books 테이블에서 Author와 Title로 구성된 기본 키를 가지고 있습니다.
로컬 보조 인덱스 PublicationYearIndex를 생성하여 같은 Author 기준으로 PublicationYear를 정렬 키로 사용합니다.
이로써 같은 저자의 책들을 출판 연도를 기준으로 쿼리할 수 있게 됩니다.
테이블의 기본 키와 다른 파티션 키 및 정렬 키를 가지는 독립적인 인덱스입니다.
기본 키와 완전히 독립적이며, 다른 파티션 키 및 정렬 키를 사용하여 다양한 쿼리 패턴을 지원합니다.
params = {
TableName: 'Orders',
AttributeDefinitions: [
{ AttributeName: 'OrderID', AttributeType: 'S' }, // 테이블 기본 키의 파티션 키
{ AttributeName: 'OrderDate', AttributeType: 'S' }, // 글로벌 보조 인덱스의 정렬 키
],
KeySchema: [
{ AttributeName: 'OrderID', KeyType: 'HASH' }, // 테이블 기본 키의 파티션 키
],
GlobalSecondaryIndexes: [
{
IndexName: 'OrderByDateIndex',
KeySchema: [
{ AttributeName: 'OrderDate', KeyType: 'HASH' }, // 글로벌 보조 인덱스의 파티션 키
],
Projection: { ProjectionType: 'ALL' }, // 모든 어트리뷰트를 포함
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5,
},
}
],
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5,
},
};
Orders 테이블에서 OrderID로 구성된 기본 키를 가지고 있습니다.
글로벌 보조 인덱스 OrderByDateIndex를 생성하여 OrderDate를 정렬 키로 사용합니다.
이로써 주문을 주문 날짜를 기준으로 쿼리할 수 있게 됩니다.
AttributeDefinitions와 KeySchema는 DynamoDB 테이블을 생성할 때 필요한 설정입니다. 이 두 요소는 테이블의 구조를 정의하고 데이터를 저장하는 방식을 결정하는 데 중요한 역할을 합니다.
테이블에 저장될 각 아이템의 어트리뷰트(속성 또는 필드)를 정의합니다.
각 어트리뷰트의 데이터 유형을 지정하며, 이는 해당 어트리뷰트에 저장될 데이터의 형식을 나타냅니다.
예를 들어, 문자열인지 숫자인지를 지정할 수 있습니다.
필요한 경우 인덱스나 기타 기능들에 대한 설정도 여기에 추가될 수 있습니다.
테이블의 주요 키(Primary Key)를 정의합니다.
주요 키는 테이블 내의 각 아이템을 고유하게 식별하는 데 사용됩니다.
주요 키는 하나 이상의 어트리뷰트로 구성되며, 이를 통해 DynamoDB는 데이터를 저장하고 쿼리할 때 어떻게 효율적으로 수행할지 결정합니다.
주로 해시 키(Hash Key)와 범위 키(Range Key)로 구성됩니다
tableDefinition = {
TableName: 'UserTable',
AttributeDefinitions: [
{ AttributeName: 'UserID', AttributeType: 'S' },
{ AttributeName: 'Timestamp', AttributeType: 'N' },
],
KeySchema: [
{ AttributeName: 'UserID', KeyType: 'HASH' }, // UserID를 해시 키로 사용
{ AttributeName: 'Timestamp', KeyType: 'RANGE' }, // Timestamp를 범위 키로 사용
],
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5,
},
};
AWS SDK를 사용하며 api를 통해 crud를 수행합니다. tableDefinition을 기반
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB();
const createItem = async () => {
const params = {
TableName: 'UserTable',
Item: {
'UserID': { S: 'user123' },
'Timestamp': { N: '1641578400' },
'Name': { S: 'John Doe' },
'Email': { S: 'john.doe@example.com' },
},
};
try {
const result = await dynamodb.putItem(params).promise();
console.log('Item created successfully:', result);
} catch (error) {
console.error('Error creating item:', error);
}
};
const getItem = async () => {
const params = {
TableName: 'UserTable',
Key: {
'UserID': { S: 'user123' },
'Timestamp': { N: '1641578400' },
},
};
try {
const result = await dynamodb.getItem(params).promise();
console.log('Item retrieved successfully:', result.Item);
} catch (error) {
console.error('Error retrieving item:', error);
}
};
const updateItem = async () => {
const params = {
TableName: 'UserTable',
Key: {
'UserID': { S: 'user123' },
'Timestamp': { N: '1641578400' },
},
UpdateExpression: 'SET #name = :newName',
ExpressionAttributeNames: { '#name': 'Name' },
ExpressionAttributeValues: {
':newName': { S: 'Updated Name' },
},
ReturnValues: 'ALL_NEW',
};
try {
const result = await dynamodb.updateItem(params).promise();
console.log('Item updated successfully. New data:', result.Attributes);
} catch (error) {
console.error('Error updating item:', error);
}
};
const deleteItem = async () => {
const params = {
TableName: 'UserTable',
Key: {
'UserID': { S: 'user123' },
'Timestamp': { N: '1641578400' },
},
};
try {
const result = await dynamodb.deleteItem(params).promise();
console.log('Item deleted successfully:', result);
} catch (error) {
console.error('Error deleting item:', error);
}
};
DynamoDB에 전달되는 업데이트 표현식에서 사용되는 플레이스홀더와 값에 해당합니다. 이러한 구성은 업데이트가 부분적으로 실행되지 않도록 보장합니다.
페이로드 내에서 여러 작업(SET, REMOVE, ADD, DELETE)에 대한 표현식을 생성합니다. 이때 각 작업은 원자적으로 수행되어야 함을 고려합니다.
UpdateItem 작업을 수행하기 전에 조건 표현식(Conditional Expression)을 사용하여 특정 조건이 충족되었을 때만 업데이트를 허용하도록 설정할 수 있습니다. 이는 업데이트가 특정 조건을 만족하지 않으면 실패하도록 만듭니다.
const params = {
TableName: 'UserTable',
Key: {
'UserID': { S: 'user123' },
'Timestamp': { N: '1641578400' },
},
UpdateExpression: 'SET #name = :newName',
ConditionExpression: '#name = :oldName',
ExpressionAttributeNames: {
'#name': 'Name',
},
ExpressionAttributeValues: {
':newName': { S: 'Updated Name' },
':oldName': { S: 'Current Name' },
},
ReturnValues: 'ALL_NEW',
};
DynamoDB는 트랜잭션을 지원하며, 여러 작업을 단일 트랜잭션으로 그룹화하여 원자성을 보장할 수 있습니다. 하나의 트랜잭션에서 여러 개의 UpdateItem 또는 다른 작업을 수행할 수 있습니다.
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB();
const updateWithTransaction = async () => {
const transactionParams = {
TransactItems: [
{
Update: {
TableName: 'UserTable',
Key: {
'UserID': { S: 'user123' },
'Timestamp': { N: '1641578400' },
},
UpdateExpression: 'SET #name = :newName',
ConditionExpression: '#name = :oldName',
ExpressionAttributeNames: {
'#name': 'Name',
},
ExpressionAttributeValues: {
':newName': { S: 'Updated Name' },
':oldName': { S: 'Current Name' },
},
ReturnValues: 'ALL_NEW',
},
},
// Add more TransactItems if needed
],
};
try {
const result = await dynamodb.transactWriteItems(transactionParams).promise();
console.log('Transaction executed successfully:', result);
} catch (error) {
console.error('Error executing transaction:', error);
}
};
DynamoDB에서 데이터를 읽어오는 2가지 방식
Query는 DynamoDB 테이블에서 특정 파티션 키 및 정렬 키 값을 기반으로 항목을 읽어오는 작업입니다.
기본 키에 해당하는 값을 제공하면 DynamoDB는 해당 파티션 키에 속하는 항목 중 일치하는 정렬 키 값을 찾아 반환합니다.
주로 정렬된 데이터에서 원하는 항목을 조회할 때 사용합니다.
params = {
TableName: 'Books',
KeyConditionExpression: 'Author = :author AND Title = :title',
ExpressionAttributeValues: {
':author': 'John Doe',
':title': 'Introduction to DynamoDB',
},
};
dynamodb.query(params, (err, data) => {
if (err) console.error(err);
else console.log(data.Items);
});
Scan은 DynamoDB 테이블의 전체 항목을 검색하는 작업입니다.
특정 조건을 지정하지 않으면 전체 테이블을 스캔하게 되므로 주의가 필요합니다.
주로 특정 쿼리 조건이 없거나, 테이블의 일부 데이터를 불규칙적으로 조회해야 할 때 사용합니다.
params = {
TableName: 'Orders',
FilterExpression: 'OrderStatus = :status',
ExpressionAttributeValues: {
':status': 'Shipped',
},
};
dynamodb.scan(params, (err, data) => {
if (err) console.error(err);
else console.log(data.Items);
});
Query는 기본 키 값으로 특정 항목을 신속하게 가져오는 데에 사용되며, 정렬된 테이블에서 가장 효과적입니다.
Scan은 전체 테이블을 스캔하므로 비용과 성능에 영향을 미칠 수 있습니다. 최대한 Query를 사용하여 정확한 데이터만을 검색하도록 노력하는 것이 좋습니다.
DynamoDB에서의 페이지네이션은 Query 또는 Scan 작업에서 결과 집합이 큰 경우에 이를 처리하는 방법입니다. 큰 결과 집합을 모두 한 번에 가져오는 것은 비용과 성능 면에서 비효율적일 수 있으므로, 페이지네이션을 통해 일부 결과만 가져오고 다음 페이지로 넘어가는 것이 일반적입니다.
Query 또는 Scan 작업을 수행할 때 Limit 매개변수를 사용하여 가져올 아이템의 최대 개수를 지정할 수 있습니다. 또한, ExclusiveStartKey를 사용하여 특정 키에서부터 시작하여 결과를 가져올 수 있습니다.
결과 집합이 여전히 다음 페이지에 대한 키가 있을 경우, 이전 페이지에서 얻은 LastEvaluatedKey를 사용하여 새로운 Query 또는 Scan 작업을 수행합니다. 이를 통해 페이지를 계속해서 가져올 수 있습니다.
LastEvaluatedKey가 더 이상 반환되지 않을 때까지 페이지를 반복적으로 가져옵니다. 이 때, 각 페이지의 LastEvaluatedKey를 저장하고 이를 이용해 다음 페이지를 가져오는 방식으로 전체 결과 집합을 처리합니다.
const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
async function paginateQuery() {
const params = {
TableName: 'YourTableName',
KeyConditionExpression: 'YourKeyConditionExpression',
Limit: 10, // Number of items to fetch per page
};
let lastEvaluatedKey = null;
do {
if (lastEvaluatedKey) {
params.ExclusiveStartKey = lastEvaluatedKey;
}
const result = await dynamoDB.query(params).promise();
const items = result.Items;
// Process the items on the current page
// Set lastEvaluatedKey for the next iteration
lastEvaluatedKey = result.LastEvaluatedKey;
} while (lastEvaluatedKey);
}
// Call the pagination function
paginateQuery();