DynamoDB 开发 - JS

DynamoDB in ‘node.js’

DynamoDB Local

本地运行DynamoDB作为开发环境, 下载后本地解压运行:

1
java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb

aws-sdk

依赖于aws-sdk:

1
npm install --save aws-sdk

注意: DynamoDB Local和 DynamoDB Web 服务是有差异的: https://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html

Dev

查看已存在的表

1
2
3
4
5
6
7
8
9
var DynamoDb = new AWS.DynamoDB();

DynamoDb.listTables(function (err, data) {
if (err) {
console.error("Unable to list tables. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log(data);
}
});

创建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var DynamoDb = new AWS.DynamoDB();

var tableSchema = {
TableName: "Movies"
KeySchema: [
{AttributeName: "year", KeyType: "HASH"}, //Partition key
{AttributeName: "title", KeyType: "RANGE"} //Sort key
],
AttributeDefinitions: [
{AttributeName: "year", AttributeType: "N"},
{AttributeName: "title", AttributeType: "S"}
],
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 10
}
}

DynamoDb.createTable(tableSchema, function (err, data) {
if (err) {
console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log(data);
}
});

注意: KeySchema - 必须有且仅有一个分区键, 排序键可以没有, 但最多只有一个.
表的大小没有实际限制。表的项目数和字节数是无限制的
对于任何 AWS 账户,每个区域的初始限制为 256 个表

全局表
全局表 为部署多区域、多主机数据库提供了完全托管的解决方案,而不必构建和维护您自己的复制解决方案。在创建全局表时,指定要在其中提供表的 AWS 区域。DynamoDB 执行在这些区域中创建相同的表并将持续数据更改传播到所有这些表所必需的所有任务。

查看表的详情

1
2
3
4
5
6
7
8
var DynamoDb = new AWS.DynamoDB();
DynamoDb.describeTable(table, function (err, data) {
if (err) {
console.error("Unable to describe table#", table.TableName, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log(data);
}
});

一般输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
"Table": {
"AttributeDefinitions": [
{
"AttributeName": "year",
"AttributeType": "N"
},
{
"AttributeName": "title",
"AttributeType": "S"
}
],
"TableName": "Movies",
"KeySchema": [
{
"AttributeName": "year",
"KeyType": "HASH"
},
{
"AttributeName": "title",
"KeyType": "RANGE"
}
],
"TableStatus": "ACTIVE",
"CreationDateTime": "2018-03-28T02:49:45.326Z",
"ProvisionedThroughput": {
"LastIncreaseDateTime": "1970-01-01T00:00:00.000Z",
"LastDecreaseDateTime": "1970-01-01T00:00:00.000Z",
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 10,
"WriteCapacityUnits": 10
},
"TableSizeBytes": 28,
"ItemCount": 1,
"TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/Movies"
}
}

添加项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var DynamoDBDocClient = new AWS.DynamoDB.DocumentClient();

var model = {
TableName: "Movies"
Item: {
"year": 2018,
"title": "Rush 1",

"info": "Info 1"
}
};
DynamoDBDocClient.put(record, function (err, data) {
if (err) {
console.error("Unable to add ", record.TableName, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log(data);
}
});

注意: DocumentClient.put方法无返回, 除非出错了

查询和扫描

Query 操作基于主键值查找项目.
Scan 操作读取表或二级索引中的每个项目 (全部的).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var DynamoDBDocClient = new AWS.DynamoDB.DocumentClient();

var model = {
TableName: "Movies"
KeyConditionExpression: "#yr = :yyyy",
ExpressionAttributeNames: {
"#yr": "year"
},
ExpressionAttributeValues: {
":yyyy": 2018
}
};

DynamoDBDocClient.query(params, function (err, data) {
if (err) {
console.error("Failed to search ", tableName, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log('data: ', data)
}
});

返回如下

1
2
3
4
5
{
Items: [{title: 'Rush 1', year: 2018, info: 'Info 1'}],
Count: 1,
ScannedCount: 1
}

Query

graph BT subgraph AWS DynamoDB D1(Apply KeyConditionExpression) D2(Apply Page Size - Limit) D3(Apply 1M Limitation) D4(Apply FilterExpression) D1 --> D2 D2 --> D3 D3 --> D4 end A[Query/Scan] --> |request|D1 D4 --> |Response|B[Result
Pagination: LastEvaluatedKey & ExclusiveStartKey] B -. Next Page .- A

更新表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var DynamoDb = new AWS.DynamoDB();

var tableSchema = {
TableName: "Movies"
AttributeDefinitions: [
{"AttributeName": "flag", "AttributeType": "N"},
{"AttributeName": "title", "AttributeType": "S"}
],
GlobalSecondaryIndexUpdates: [
{
"Create": {
"IndexName": "flag_title",
"KeySchema": [
{
"AttributeName": "flag",
"KeyType": "HASH"
},
{
"AttributeName": "title",
"KeyType": "RANGE"
}
],
"Projection": {
"NonKeyAttributes": ["info"],
"ProjectionType": "INCLUDE"
},
"ProvisionedThroughput": {
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
}
}
}
]
}
DynamoDb.updateTable(tableSchema, function (err, data) {
if (err) {
console.error("Unable to update table. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log(data);
}
});

更新项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var DynamoDBDocClient = new AWS.DynamoDB.DocumentClient();
var record = {
TableName: "Movies"
Key: {
"year": 2018,
"title": 'Wangy Test'
},
ConditionExpression: "#i = :iv",
ExpressionAttributeNames: {
"#f": "flag",
"#s": "size",
"#i": "info"
},
ExpressionAttributeValues: {
":fv": 1,
":sv": 150,
":iv": 'Wangy Test'
},

UpdateExpression: "set #f = :fv, #s = :sv",
ReturnValues:"UPDATED_NEW"
};

DynamoDBDocClient.update(record, function (err, data) {
if (err) {
console.error("Unable to update ", record.TableName, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log(data);
}
});

可以操作项:

  1. SET - 修改或添加项目属性
  2. REMOVE - 从项目中删除属性
  3. ADD - 更新数字和集合
  4. DELETE - 从集合中删除元素

参考:

  1. https://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/APIReference/API_UpdateItem.html#API_UpdateItem_RequestSyntax
  2. https://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html

删除项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var DynamoDBDocClient = new AWS.DynamoDB.DocumentClient();
var record = {
TableName: "Movies"
Key: {
"year": 2018,
"title": 'Wangy Test'
},
ConditionExpression: "#yr = :yyyy",
ExpressionAttributeNames: {
"#yr": "year"
},
ExpressionAttributeValues: {
":yyyy": 2018
}
};

DynamoDBDocClient.delete(record, function (err, data) {
if (err) {
console.error("Unable to delete ", record.TableName, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log(data);
}
});

删除表

1
2
3
4
5
6
7
8
9
10
11
var DynamoDBDocClient = new AWS.DynamoDB.DocumentClient();
var table = {
TableName: "Movies"
};
DynamoDb.deleteTable(table, function (err, data) {
if (err) {
console.error("Unable to drop table. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log('table desc: ', data)
}
});

延伸

数据类型

以下是 DynamoDB 数据类型描述符的完整列表:

  1. S – 字符串
  2. N – 数字
  3. B – 二进制
  4. BOOL – 布尔值
  5. NULL – Null
  6. M – 映射
  7. L – 列表
  8. SS – 字符串集
  9. NS – 数字集
  10. BS – 二进制集

属性分类:

  1. 标量类型 - 标量类型可准确地表示一个值。标量类型包括数字、字符串、二进制、布尔值和 null。

    1. S – 字符串
    2. N – 数字
      数字可为正数、负数或零。数字最多可精确到 38 位。超过此位数将导致异常。
      1. 正数范围:1E-130 到 9.9999999999999999999999999999999999999E+125
      2. 负数范围:-9.9999999999999999999999999999999999999E+125 到 -1E-130
        数字以可变长度形式表示。系统会删减开头和结尾的 0.
    3. B – 二进制
      1. 二进制属性的长度必须大于零且受限于最大 DynamoDB 项目大小 400 KB
      2. 将主键属性定义为二进制类型属性,以下附加限制将适用:
        1. 对于简单的主键,第一个属性值 (分区键) 的最大长度为 2048 字节。
        2. 对于复合主键,第二个属性值 (排序键) 的最大长度为 1024 字节。
      3. 在将二进制值发送到 DynamoDB 之前,您的应用程序必须采用 Base64 编码格式对其进行编码。收到这些值后,DynamoDB 会将数据解码为无符号字节数组,将其用作二进制属性的长度
    4. BOOL – 布尔值
    5. NULL – Null
      空代表属性具有未知或未定义状态。
  2. 文档类型 - 文档类型可表示具有嵌套属性的复杂结构 - 例如您将在 JSON 文档中找到的结构。文档类型包括列表和映射。
    文档类型包括列表和映射。这些数据类型可以互相嵌套,用来表示深度最多为 32 层的复杂数据结构。
    只要包含值的项目大小在 DynamoDB 项目大小限制 (400 KB) 内,列表或映射中值的数量就没有限制。
    属性值不能是空字符串或空集 (字符串集、数字集或二进制集),但可以是空列表和映射。

    1. M – 映射
      映射类型属性可以存储名称/值对的无序集合。映射用大括号括起:{ … }
      映射类似于 JSON 对象。映射元素中可以存储的数据类型没有限制,映射中的元素也不一定为相同类型
    2. L – 列表
      列表类型属性可存储值的有序集合。列表用方括号括起:[ … ]
      列表类似于 JSON 数组。列表元素中可以存储的数据类型没有限制,列表元素中的元素也不一定为相同类型。
  3. 集类型 - 集类型可表示多个标量值。集类型包括字符串集、数字集和二进制集。
  4. 主键属性定义为字符串类型属性,以下附加限制将适用:
    1. 对于简单的主键,第一个属性值 (分区键) 的最大长度为 2048 字节。
    2. 对于复合主键,第二个属性值 (排序键) 的最大长度为 1024 字节。

日期

DynamoDB没有日期类型, 解决方案:

  1. 使用字符串数据类型表示日期或时间戳。执行此操作的一种方法是使用 ISO 8601 字符串,如以下示例所示:
    1. 2016-02-15
    2. 2015-12-21T17:42:34Z
    3. 20150311T122706Z
      有关更多信息,请访问 http://en.wikipedia.org/wiki/ISO_8601
  2. 使用数字数据类型表示日期或时间戳。执行此操作的一种方法是使用纪元时间 - 自 1970 年 1 月 1 日 00:00:00 UTC 以来的秒数。例如,纪元时间 1437136300 表示 2015 年 7 月 17 日 12:31:40 UTC。
    有关更多信息,请访问 http://en.wikipedia.org/wiki/Unix_time。

命名个规则

  1. DynamoDB 中的表、属性和其他对象必须具有名称.
    所有名称都必须使用 UTF-8 进行编码,并且区分大小写。
    表名称和索引名称的长度必须介于 3 到 255 个字符之间,而且只能包含以下字符:

    1. a-z
    2. A-Z
    3. 0-9
    4. _ (下划线)
      • (短划线)
    5. . (圆点)

    属性名称的长度必须介于 1 到 255 个字符之间。
    具有特殊含义:# (散列) 和 : (冒号)
    保留关键字: https://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/ReservedWords.html

读取一致性

  1. Amazon DynamoDB 在全世界多个 AWS 区域可用。每个区域均与其他 AWS 区域独立和隔离
  2. 每个 AWS 区域包含多个不同的称为“可用区”的位置。每个可用区都与其他可用区中的故障隔离,并提供与同一区域其他可用区的低成本、低延迟网络连接。这使您可以在某个区域的多个可用区之间快速复制数据。
  3. 当您的应用程序将某个数据写入 DynamoDB 表并收到 HTTP 200 响应 (OK) 时,该数据的所有副本都会更新。该数据最终将在所有存储位置中保持一致,通常只需一秒或更短时间。
  4. 最终一致性读取
    当您从 DynamoDB 表中读取数据时,响应反映的可能不是刚刚完成的写入操作的结果。响应可能包含某些陈旧数据。如果您在短时间后重复读取请求,响应将返回最新的数据。
  5. 强一致性读取
    当您请求强一致性读取时,DynamoDB 会返回具有最新数据的响应,从而反映来自所有已成功的之前写入操作的更新。如果网络延迟或中断,可能会无法执行强一致性读取。
    读取操作 (例如 GetItem,Query 和 Scan) 提供了一个 ConsistentRead 参数。如果您将此参数设置为 true,DynamoDB 将在操作过程中使用强一致性读取。

Count问题

https://stackoverflow.com/questions/12499822/how-can-i-get-the-total-number-of-items-in-a-dynamodb-table