你好,我是猿java。
上篇文章我们分享了DynamoDB的本地二级索引,既然有了本地二级索引,为什么还需要全局二级索引?两者之间有什么相似点和区别呢?今天我们就来一起聊聊。
概念
全局二级索引本质上是一种数据结构(类同于mysql中的索引的概念)。每个全局二级索引必须和一个表关联,这个表称为索引的基表,索引可以包含基表中的某些或者全部属性。
如下图,可以把索引看作和表一样的数据结构,这样我们就能可以使用Query或者Scan从索引中检索数据。
创建二级索引时,从基表投影或复制到索引中的属性,一般包含三种模式:
- 所有字段模式:基础表的所有属性都投影到索引中
- 只有keys模式:只有索引和主键被投影到索引中
- 包含模式:索引和主键以及额外指定的其他非键属性
为什么需要全局二级索引
比如下面的一张User表,Partition key为Id,Sort key为UserId,查询的场景是:Age为18,Name包含’张’的所有用户,我们就不得不全表扫描表,当表的数据达到千万甚至更大时,
这将会消耗大量的时间,如何快速的查询结果?给Age和Name字段增加一个索引,这就是DynamoDB的二级索引。
全局二级索引的创建
创建全局二级索引的方式有多种,这里提供3种常见的方式:控制台、aws指令、代码。
控制台
控制台的有点就是能可视化创建,操作可以参考下图:
这里的Partition key和基表复合主键中的Partition key不能相同,Sort key为可选项,比如:我这里填写的是Age, 点击Create index按钮,全局二级索引就创建完成:
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代码例子如下:
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); 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,持续输出硬核文章。