DynamoDB系列之--全局二级索引

你好,我是猿java。

上篇文章我们分享了DynamoDB的本地二级索引,既然有了本地二级索引,为什么还需要全局二级索引?两者之间有什么相似点和区别呢?今天我们就来一起聊聊。

概念

全局二级索引本质上是一种数据结构(类同于mysql中的索引的概念)。每个全局二级索引必须和一个表关联,这个表称为索引的基表,索引可以包含基表中的某些或者全部属性。
如下图,可以把索引看作和表一样的数据结构,这样我们就能可以使用Query或者Scan从索引中检索数据。

img.png

创建二级索引时,从基表投影或复制到索引中的属性,一般包含三种模式:

  1. 所有字段模式:基础表的所有属性都投影到索引中
  2. 只有keys模式:只有索引和主键被投影到索引中
  3. 包含模式:索引和主键以及额外指定的其他非键属性

img.png

为什么需要全局二级索引

比如下面的一张User表,Partition key为Id,Sort key为UserId,查询的场景是:Age为18,Name包含’张’的所有用户,我们就不得不全表扫描表,当表的数据达到千万甚至更大时,
这将会消耗大量的时间,如何快速的查询结果?给Age和Name字段增加一个索引,这就是DynamoDB的二级索引。

全局二级索引的创建

创建全局二级索引的方式有多种,这里提供3种常见的方式:控制台、aws指令、代码。

控制台

控制台的有点就是能可视化创建,操作可以参考下图:

img.png
img.png

这里的Partition key和基表复合主键中的Partition key不能相同,Sort key为可选项,比如:我这里填写的是Age, 点击Create index按钮,全局二级索引就创建完成:

img.png

aws指令创建

aws指令是亚马逊提供的一套本地操作产品的指令,有点类似mysql的client指令,我们可以参考官方文档进行安装

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
42
43
44
45
46
47
48
$ aws dynamodb create-table \
--table-name test \
--attribute-definitions '[
{
"AttributeName": "Id",
"AttributeType": "S"
},
{
"AttributeName": "Age",
"AttributeType": "N"
},
{
"AttributeName": "Name",
"AttributeType": "S"
}
]' \
--key-schema '[
{
"AttributeName": "Id",
"KeyType": "HASH"
},
{
"AttributeName": "Age",
"KeyType": "RANGE"
}
]' \
--global-secondary-indexes '[
{
"IndexName": "Age-index",
"KeySchema": [
{
"AttributeName": "Id",
"KeyType": "HASH"
},
{
"AttributeName": "Age",
"KeyType": "RANGE"
}
],
"Projection": {
"ProjectionType": "KEYS_ONLY"
}
}
]' \
--provisioned-throughput '{
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
}' \

代码

1
2
3
4
5
6
7
GlobalSecondaryIndex precipIndex = new GlobalSecondaryIndex()
.withIndexName("PrecipIndex")
.withProvisionedThroughput(new ProvisionedThroughput()
.withReadCapacityUnits((long) 10)
.withWriteCapacityUnits((long) 1))
.withProjection(new Projection().withProjectionType(ProjectionType.ALL));

全局二级索引的使用

在上文中我们创建了一个全局二级索引 Age-Name-index,我们需要使用该全局二级索引从test表中查询age=30,名字=张三的所有用户,java代码例子如下:

img.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
public Optional<PublicResourceModel> findByUserIdResourceId(String userId, String resourceId) {
HashMap<String, AttributeValue> eav = Maps.newHashMap();
eav.put(":age", new AttributeValue().withS(userId));
eav.put(":name", new AttributeValue().withS(resourceId));

DynamoDBQueryExpression<PublicResourceModel> queryExpression = new DynamoDBQueryExpression<>();
queryExpression.setIndexName("Age-Name-index");
queryExpression.setConsistentRead(false);
queryExpression.withKeyConditionExpression("Age= :age and Name= :name");
queryExpression.withExpressionAttributeValues(eav);
queryExpression.withLimit(1);
// 因为全局二级索引中的数据,key-value不需要唯一,所以这里可能会查出一个列表
PaginatedQueryList<PublicResourceModel> list = SpringContextUtil.getDynamoDBMapper().query(PublicResourceModel.class, queryExpression);
if(!CollectionUtils.isEmpty(list)){
return Optional.of(list.get(0));
}
return Optional.empty();
}
}

注意事项

  • 全局二级索引与基表拥有不同的分区键(Partition key), 排序键(Sort key)可以相同也可以不相同;
  • 全局二级索引可以在创建基表时创建,也能在现有表上去添加;
  • DynamoDB会自动维护全局二级索引,当表中有增、删、改操作时,DynamoDB会利用最终一致的模型异步把数据的变动维护到索引中;
  • 全局二级索引将会继承基表的读写容量(读写容量是在创建表示设置的参数)
  • 创建全局二级索引时,指定了分区键和排序键的类型,因此在往基表中增加数据时,类型必须和全局二级索引中字段类型保持一致,否则抛出ValidationException异常;
  • 全局二级索引越多花费的费用越多

全局二级索引和本地二级索引的比较

本地二级索引必须和基表拥有相同的Partition key,而全局二级索引和基表的Partition key不能相同;
本地二级索引对应的基表必须是符合主键,而全局二级索引不做限制;
本地二级索引必须和基表一起创建,而全局二级索引可以和基表一起创建,也可以在基表创建好之后创建;

参考文档

AWS DynamoDb官方文档

学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

drawing