Friday, December 30, 2016

AWS DynamoDB

DynamoDB是AWS产品线之一,它有如下特点:
1. NoSQL
2. schema less设计。也就是每个item(行)的列名不固定可省空间。原理是每item按json存。
3. 只能query(partition)/sort (range key),不能query/sort非主键。除非创建GSI(global secondary index)
4. 计价方式颠覆传统数据库。传统用空间大小计费,而DynamoDB用request峰值计算
5. 存储按partition key来分配存储位置,不用像传统一样做增加空间、shrink、备份等DBA工作
6. 每份数据至少有3个备份

新特性:
1. 无需规划容量read/write capacity,采用按请求付费的定价模式
2. 支持事务await dynamoDb.transactWriteItems
3. Accelerator - dynamoDB的cache


架构

重点:
存储key-value和更改trigger(stream)

核心概念:
Table:与传统DB一样
Item:传统DB的行
Attribute:传统DB的列名
Primary key:是必须的。有两种primary key:一种只用partition key,此时partition key在全表中唯一。另一种是partition key+sort key作为组合主键(如名字+生日唯一确定一个人)。数据时按partition key来存储的,同一partition内,数据按sort key排序。partition key叫hash attribute只能用于相等性比较,而sort key叫range attribute,可以进行相等和大小比较。
Secondary Indexes:额外索引来提高非主键数据读取速度,由于primary key已是索引,所以这里叫额外索引。索引包括全局和局部索引。全局索引针对全表,如studentID(HashKey), courseID(SortKey),  score(IndexHashKey)可以获得某个分数的所有人。局部索引是针对每个partition key的索引,类似于SQL Server的filter index。指定index时还要选include primary key还是其他非key属性或是全部,类似与SQL Server中index的include。但是数据一致性并不能保证。写入新数据,可能需要一些时间才会写到二级索引。
read capacity:默认的read capacity为5,表示每秒只允许5个读操作。

最佳实践
CID, DeviceId+type
EntityType, entityId
如果存epoch time,用Number存
customerid+startPoint作为hash key,endPoint做range Key,再对startPoint做secondary index

底层API:

创建table如下,dynamoDB.getTable("Movies").putItem, getItem, batchGetItem, batchWriteItem, query, scan, delete,这是dynamoDB的API属于low level API。

 AmazonDynamoDBClient client = new AmazonDynamoDBClient()
            .withEndpoint("http://localhost:8000");

 DynamoDB dynamoDB = new DynamoDB(client);

 Table table = dynamoDB.createTable(“Movies”,
                Arrays.asList(
                    new KeySchemaElement("year", KeyType.HASH),  //Partition key
                    new KeySchemaElement("title", KeyType.RANGE)), //Sort key
                    Arrays.asList(
                        new AttributeDefinition("year", ScalarAttributeType.N),
                        new AttributeDefinition("title", ScalarAttributeType.S)),
                    new ProvisionedThroughput(10L, 10L));
            table.waitForActive();

封装层 - Mapper:

AWS SDK
Developer Guide中的例子:

代表一个item:
@DynamoDBTable(tableName="Music")
public class MusicItem {
    private String artist;
    private String songTitle;
    private String albumTitle;
    private int year;

    @DynamoDBHashKey(attributeName="Artist")
    public String getArtist() { return artist;}
    public void setArtist(String artist) {this.artist = artist;}

    @DynamoDBRangeKey(attributeName="SongTitle")
    public String getSongTitle() { return songTitle;}
    public void setSongTitle(String songTitle) {this.songTitle = songTitle;}

    @DynamoDBAttribute(attributeName = "AlbumTitle")
    public String getAlbumTitle() { return albumTitle;}
    public void setAlbumTitle(String albumTitle) {this.artist = albumTitle;}
}

@DynamoDBAttribute不是必须,与lombok连用:

@DynamoDBTable(tableName="Music")
@Data
public class MusicItem {
     @DynamoDBHashKey(attributeName="Artist")
      private String artist;
      private String albumTitle;

      @DynamoDBVersionAttribute
      private Integer version;
}

根据primary key读取一个item:
        AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        DynamoDBMapper mapper = new DynamoDBMapper(client);
     
        MusicItem keySchema = new MusicItem();
        keySchema.setArtist("No One You Know");
        keySchema.setSongTitle("Call Me Today");
        MusicItem result = mapper.load(keySchema);

SDK中核心类为DynamoDBMapper作为持久层的接口来操作DynamoDB,作用也类似于ORM或Hibernate作为Table和class的桥梁。DynamoDBMapper只能用于某一个table的item的创建、读、更新、删除,若要对table进行操作就要用low level API。
AmazonDynamoDBClient client = new AmazonDynamoDBClient(new ProfileCredentialsProvider());
DynamoDBMapper mapper = new DynamoDBMapper(client);
CatalogItem item = new CatalogItem();
mapper.save(item);    

query第一个参数是结果类,第二参数为query条件也是用结果类封装:
CatalogItem partitionKey = new CatalogItem();
partitionKey.setId(102);
DynamoDBQueryExpression<CatalogItem> queryExpression = new DynamoDBQueryExpression<CatalogItem>()
    .withHashKeyValues(partitionKey);
List<CatalogItem> itemList = mapper.query(CatalogItem.class, queryExpression);
for (int i = 0; i < itemList.size(); i++) {
    System.out.println(itemList.get(i).getTitle());
    System.out.println(itemList.get(i).getBookAuthors());
}

mapper的方法包括save, batchSave, load, query, delete
非DynamoDBMapper方法去读数据

Index:
DynamoDBQueryExpression<TableClass> expression = new DynamoDBQueryExpression<TableClass>()
        .withIndexName("index-name")
        .withConsistentRead(false)
        .withHashKeyValues(tableObject)

功能


操作:
GUI:创建表格、增加item都可以通过AWS界面完成

Table区域:
table按区域,也就是east-2有table,west-1就不会有。

Auto scaling:
30min才会生效。

GSI/LSI:
GSI是任何两个属性,而LSI的分区键与原分区键一致,sort key不同。GSI是不保证强一致性,LSI则可以。

乐观锁:
指定一个属性作为版本号。如果保存,版本号不一致,更新失败。用意在于确保,更新的改动不会被其他transaction覆盖。具体实现是客户端版本号+1,输入后端,若match DDB中版本号+1,就成功更新,返回整个item包括新版本号。否则不成功。

PutItem:
覆盖现有item。但可以用条件先判断是否存在。

TTL:
每个item的生存时间(time to live),dynamoDB会48小时内删除过期的项目。用户修改table属性指定TTL的attribute,然后写入item时候用户指定过期时间如currentTime+5 days. 

DDB streams:
用例有DB不同区域sync,DDB数据分析,新用户通知。一个stream是一个改动,只能保留24小时。Kinesis是DDB streams客户端适配器。
DDB stream->lambda->SNS->notification

DDB stream默认设置
Retry = -1 无限重试 (这个retry控制lambda的retry,lambda本身的retry不起作用,所以lambda设的DLQ也没用)
Maximum age of event = -1 无时间限制,event永远留在Stream
https://enrico-portolan.medium.com/how-aws-lambda-retry-really-works-fb823e78b4a1

全局表:
跨区的相同表结构,不保证强一致性,只保证最终一致性。

静态加密:
加密敏感数据。

事务:
仅支持10个项目

DAX:
Cache层,若需要强一致性,不推荐使用。从10毫秒降到微秒。

QueryFilter
用于DAO层的实现如contains, id=(:id).

On-demand mode
provisioned会让request throttled,即使设置了auto-scaling也会有delay。但on-demand就自动30分钟增加两倍,对于6000以下RPU的,就可以设置这个。因为初始为6000。 

最佳实践:
多对多关系:同一个表中,用自join存储。

Export/Import:
用data pipeline来export到S3. 不要设onTerminate的时间,因为大概要过10分钟才会真正开始,若设置了时间过短,就不会开始。

常见问题

resource not found exception
由于throttling,提高read capacity可以解决


AWS CLI:

shorthand syntax:
aws dynamodb create-table --table-name MusicCollection --attribute-definitions AttributeName=Artist,AttributeType=S AttributeName=SongTitle,AttributeType=S --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5

CLI比较方便,但文档不足,也可以用于其他AWS产品如S3。或者backup到S3再import


Ref:
官方指南(中文)
DynamoDB深度体验
DynamoDB使用经验

No comments:

Post a Comment