DynamoDB para noobs como yo
10 segundos de fama
- Andrés Santos
- Co-Organizador
-
Senior Software Engineer
- Backend Node + AWS
@dresrok
@EpamAnywhere
@LibertyMutual
@IbaguéJS
Antes de empezar...
- Tipos de Bases de Datos
- Teorema CAP
Tipos de Bases de Datos
Fuente: Graph Databases in Action - Dave Bechberger & Josh Perryman
Teorema CAP
Los sistemas distribuidos no pueden garantizar a la vez que haya Consistencia, Disponibilidad y Tolerancia a Particiones.
Consistency: Todos los nodos ven los mismos datos simultáneamente. En cada lectura se retorna la escritura más reciente.
Teorema CAP
Availability: Cada cliente SIEMPRE puede leer y escribir. En el caso de las lecturas el valor puede que no sea el más reciente.
Teorema CAP
Partition Tolerance: El sistema continúa operando a pesar de fallas de en red.
Teorema CAP
Fuente: Bases de Datos para el Big Data - Marlon Cardenas
Danos el código
Oblíguenme
Empecemos...
- ¿Qué es DynamoDB?
- ¿Cómo funciona?
- ¿Cuándo no debe usarse?
- Conceptos Generales
- DynamoDB API
¿Qué es DynamoDB?
- NoSQL rápida y flexible (key-value, schemaless)
- Operaciones de lectura y escritura en ms
- Escalamiento infinito sin degradación del rendimiento
- Totalmente gestionada
- Ofrece API, Autenticación e Infraestructura como código (IaC)
¿Cómo funciona?
Primary Key = Partition Key
+ Sort Key
¿Cuándo no debe usarse?
- Cuando tu aplicación necesite queries avanzados, joins o agrupaciones.
- Para obtener rankings, donde se necesiten funciones de agregación y clasificación
- Cuando se requiere análisis en tiempo real de datos históricos
Conceptos Generales
-
Tablas
-
Atributos*
-
Items
-
Llaves Primarias*
-
Índices**
-
Streams**
-
Time-to-live (TTL)**
Conceptos Generales
Fuente: The DynamoDB Book - Alex DeBrie
Conceptos Generales
Atributos*
-
Scalars: Representan exactamente un valor.
-
string, number, binary, boolean y null
-
-
Documents: Representan una estructura compleja con valores anidados similar a un JSON.
-
list y map
-
-
Sets: Representan grupos de valores escalares.
-
string, number y binary
-
Conceptos Generales
// Scalars
{
"Name": {
"S": "Andrés Santos"
},
"Picture": {
"B": "dGhpcyB0ZXh0IGlzIGJhc2U2NC1lbmNvZGVk"
},
"Age": {
"NULL": true
},
"IsOrganizer": {
"BOOL": true
},
"Attendees": {
"N": "100.0"
}
}
// Documents
{
"Event": {
"M": {
"Name": {
"S": "IbaguéJS - Meetup Octubre"
},
"Date": {
"S": "2022-10-22T10:00:00+0500"
},
"Topics": {
"L": [
{"S": "DynamoDB para noobs como yo"},
{"S": "Redux Toolkit"}
]
}
}
}
}
// Sets
{
"Organizers": {
"SS": ["Diana", "Yeison", "Juan JS", "Jomazao"]
},
"AvgAttendance": {
"NS": ["42.2", "19", "35", "32.14"]
},
"EventImages": {
"BS": ["U3Vubnk=", "UmFpbnk=", "U25vd3k="]
}
}
Conceptos Generales
Llaves Primarias*
-
Simples: Consta de un solo elemento llamado Partition Key (Hash) cuyo valor debe ser único.
-
Compuestas: Consta de dos elementos el Partition Key y el Sort Key (Range), el valor del PK puede repetirse pero la combinación de ambos genera un identificador único.
Conceptos Básicos
Llaves Primarias*
Conceptos Básicos
// Usando SDK
const AWS = require('aws-sdk');
AWS.config.update({region: 'REGION'});
const ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
const params = {
AttributeDefinitions: [
{ AttributeName: 'Actor', AttributeType: 'S' },
{ AttributeName: 'Movie', AttributeType: 'S' }
],
KeySchema: [
{ AttributeName: 'Actor', KeyType: 'HASH' },
{ AttributeName: 'Movie', KeyType: 'RANGE' }
],
TableName: 'actors-dev',
};
ddb.createTable(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Table Created", data);
}
});
Creando una tabla
// Usando CDK
import dynamodb = require('aws-cdk-lib/aws-dynamodb');
const dbTable = new dynamodb.Table(this, 'actors-dev', {
tableName: 'actors-dev',
partitionKey: { name: 'Actor', type: 'S' },
sortKey: { name: 'Movie', type: 'S' },
});
Conceptos Básicos
Índices**
-
Local Secondary Index (LSI): Tiene el mismo PK que la tabla base pero un SK diferente.
-
Global Secondary index (GSI): Tanto el PK como el SK son diferentes que la tabla base.
Conceptos Básicos
Streams**
Fuente: The DynamoDB Book - Alex DeBrie
Conceptos Básicos
Time-to-live (TTL)**
-
Permite definir un tiempo de expiración para borrar un item de manera automática.
// Usando CDK
import dynamodb = require('aws-cdk-lib/aws-dynamodb');
const dbTable = new dynamodb.Table(this, 'actors-dev', {
tableName: 'actors-dev',
partitionKey: { name: 'Actor', type: 'S' },
sortKey: { name: 'Movie', type: 'S' },
timeToLiveAttribute: 'ExpiresAt', // Unix Timestamp
});
DynamoDB API
- PutItem
- GetItem
- UpdateItem
- DeleteItem
- Query
- Scan
PutItem
// PutItem
INSERT INTO "actors-dev" (Actor, Movie, Role, ...)
VALUES (Natalie Portman, Star Wars: Attack of the Clones, Padmé Amidala, ...);
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
Item: {
Actor: { S: 'Natalie Portman' },
Movie: { S: 'Star Wars: Attack of the Clones' },
Role: { S: 'Padmé Amidala' },
Year: { N: '2002' },
Genre: { SS: ['Epic', 'Space Opera', 'Fantasy'] },
CharacterAttributes: {
M: {
HairColor: { S: 'brown'},
SkinColor: { S: 'light'},
EyeColor: {S: 'brown'},
}
}
},
};
await new DynamoDB().putItem(params).promise();
GetItem
// PK + SK
SELECT * FROM "actors-dev"
WHERE "Actor" = 'Tom Hanks' AND "Movie" = 'Toy Story'
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
Key: {
Actor: { S: 'Tom Hanks' },
Movie: { S: 'Toy Story' },
},
ConsistentRead: true,
};
const result = await new DynamoDB().getItem(params).promise();
result.Item.Role.S // Woody
// PK
SELECT * FROM "actors-dev"
WHERE "ActorID" = 'b261bcc9-3a3f-4ef2-b62f-85311e9f474e'
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
Key: {
ActorID: { S: 'b261bcc9-3a3f-4ef2-b62f-85311e9f474e' }
},
ConsistentRead: true,
};
const result = await new DynamoDB().getItem(params).promise();
result.Item.Role.S // Woody
UpdateItem
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
Key: {
Actor: { S: 'Natalie Portman' },
Movie: { S: 'Star Wars: Attack of the Clones' },
},
UpdateExpression:
'SET #year = :year, #characterAttributes.#eyeColor :eyeColor",
ADD #picture :picture"',
ExpressionAttributeNames: {
'#year': 'Year',
"#characterAttributes": "CharacterAttributes",
"#eyeColor": "EyeColor",
"#picture": "Picture"
},
ExpressionAttributeValues: {
':year': { N: '2002' },
':eyeColor': { S: 'green' },
':picture': { B: 'cGljdHJl=' }
}
}
await new DynamoDB().updateItem(params).promise()
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
Key: {
Actor: { S: 'Tom Hanks' },
Movie: { S: 'Cast Away' },
},
UpdateExpression: 'ADD #awards :awards"',
ExpressionAttributeNames: {
'#awards': 'Awards',
},
ExpressionAttributeValues: {
':awards': {
SS: [
'Golden Globe Award for Best Actor',
'People\'s Choice Award for Favorite Movie Actor'
]
}
}
}
await new DynamoDB().updateItem(params).promise()
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
Key: {
Actor: { S: 'Natalie Portman' },
Movie: { S: 'Star Wars: Attack of the Clones' },
},
UpdateExpression: 'REMOVE #picture :picture"',
ExpressionAttributeNames: {
'#picture': 'Picture',
},
}
await new DynamoDB().updateItem(params).promise()
DeleteItem
// PK + SK
DELETE FROM "actors-dev"
WHERE "Actor" = 'Tim Allen' AND "Movie" = 'Toy Story'
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
Key: {
Actor: { S: 'Tim Allen' },
Movie: { S: 'Toy Story' },
},
};
await new DynamoDB().deleteItem(params).promise();
// PK
DELETE FROM "actors-dev"
WHERE "ActorID" = '1a0847bc-ae37-4977-ba3d-d400b4109926'
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
Key: {
ActorID: { S: '1a0847bc-ae37-4977-ba3d-d400b4109926' },
};
await new DynamoDB().deleteItem(params).promise();
Query
SELECT * FROM "actors-dev"
WHERE "Actor" LIKE 'Tom Hanks'
LIMIT 2
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
KeyConditionExpression: '#actor = :actor',
ExpressionAttributeNames: {
'#actor': 'Actor'
},
ExpressionAttributeValues: {
':actor': { 'S': 'Tom Hanks' }
},
Limit: 2
}
const result = await new DynamoDB().query(params).promise();
result.Items.forEach((item) => item.Role.S)
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
KeyConditionExpression: '#actor = :actor AND #movie BETWEEN :a AND :m',
ExpressionAttributeNames: {
'#actor': 'Actor',
'#movie': 'Movie'
},
ExpressionAttributeValues: {
':actor': { 'S': 'Tom Hanks' },
':a': { 'S': 'A' },
':m': { 'S': 'M' }
},
}
await new DynamoDB().query(params).promise();
result.Items.forEach((item) => item.Role.S)
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
KeyConditionExpression: '#actor = :actor AND begins_with(#movie, :movie)',
ExpressionAttributeNames: {
'#actor': 'Actor',
'#movie': 'Movie'
},
ExpressionAttributeValues: {
':actor': { 'S': 'Tom Hanks' },
':movie': { 'S': 'Cast' }
},
}
await new DynamoDB().query(params).promise();
result.Items.forEach((item) => item.Role.S)
// Key condition expressions for query
a = b
a < b
a <= b
a > b
a >= b
a BETWEEN b AND c
begins_with (a, substr)
Query + GSI
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
IndexName: 'MoviesIndex',
KeyConditionExpression: '#movie = :movie',
ExpressionAttributeNames: {
'#movie': 'Movie'
},
ExpressionAttributeValues: {
':movie': { 'S': 'Toy Story' }
},
}
await new DynamoDB().query(params).promise();
result.Items.forEach((item) => item.Actor.S)
// Usando CDK
import dynamodb = require('aws-cdk-lib/aws-dynamodb');
const dbTable = new dynamodb.Table(this, 'actors-dev', {
tableName: 'actors-dev',
partitionKey: { name: 'Actor', type: 'S' },
sortKey: { name: 'Movie', type: 'S' },
});
dbTable.addGlobalSecondaryIndex({
indexName: 'MoviesIndex',
partitionKey: { name: 'Movie', type: 'S'},
sortKey: { name: 'Actor', type: 'S'},
projectionType: 'ALL',
});
Query + Filter
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'customer-orders-dev',
KeyConditionExpression: '#c = :c AND #ot BETWEEN :start and :end"',
FilterExpression: '#amount > :amount',
ExpressionAttributeNames: {
'#c': 'CustomerId',
'#ot': 'OrderTime',
"#amount": "Amount"
},
ExpressionAttributeValues: {
':c': { 'S': '36ab55a589e4' },
':start': { 'S': '2020-01-10T00:00:00.000000' },
':end': { 'S': '2020-01-20T00:00:00.000000' },
":amount": { "N": "80" }
},
}
await new DynamoDB().query(params).promise();
Scan
import { DynamoDB } from 'aws-sdk';
const params = {
TableName: 'actors-dev',
FilterExpression: "#genre = :genre",
ExpressionAttributeNames: {
'#genre': 'Genre',
},
ExpressionAttributeValues: {
":genre": { "S": "Drama" }
},
}
await new DynamoDB().scan(params).promise();
DynamoDB
By Andrés Santos
DynamoDB
- 145