AWS DynamoDB教程之二:设计理念,主键的设计及GSI


作为AWS众多云服务的核心成员之一,DynamoDB得到了非常广泛的应用。下面就通过一系列教程来介绍一下DynamoDB的使用。本次主要介绍一下DynamoDB中主键Primary Key的设计考虑因素。在DynamoDB中,主键的选择太重要了,因为它会影响到之后访问数据的方式。如果设计的有问题,对于某些数据的访问将变得较为困难,或者影响性能,亦或影响使用DynamoDB的费用。

AWS DynamoDB

AWS DynamoDB系列教程:

Partition Key及Sort Key的选择

Partition分区其实上应该理解为一个逻辑上的划分。当然,在实际存储的时候可能会由于分区不同而存储在不同的存储设备上。分区的作用就是为了能够快速定位到数据。因此,在设计Partition Key的时候,应尽量将数据打散,避免hot partition,同一个Partition上的数据不宜过大,以保证高性能和避免Throttling(对于每个Partition,Up to 3000 RCU+1000 WCU)。

如果分配给表200 Read Capacity Unit (RCU),那么就意味着不同分区竟会共享这200 RCU。

如果同时选择了Sort Key,那么在同一个分区中,DynamoDB会根据Sort Key进行排序。在进行查询的时候,Sort Key可以帮助我们筛选数据。

在考虑是否需要Sort Key的时候:

  • 如果Partition Key唯一,且每次在访问DynamoDB的时候都事先知道Partition Key,那么就没有必要添加Sort Key了。
  • 如果Partition Key不是唯一的,那毫无疑问的,肯定需要Sort Key作为辅助了。
  • 如果需要根据某个字段在某个范围内查询,则需要添加一个Sort Key。
  • 对于Sort Key本身,也可以使用一个组合字符串来构建。比如:国家#省份#城市#街道。

有些时候,为了提高性能,需要尽量将Partition Key均匀分布,避免”hot key”。

Partition Key和Sort Key的组合就是对数据进行分类,分类,再分类的过程,这样才能保证DynamoDB的高性能和扩展性。

需要注意,PK字段中是区分大小写的,因此在应用程序中应统一进行处理(转为大/小写)之后再保存。

再考虑DynamoDB使用时,可以将其视为一个字典,因此在查询时可以把查字典作为类比:

  • 查找所有以Dec开始的条目,非常快
  • 查找所有以ber结束的条目,非常慢
  • 查找所有包含abc的条目,非常慢

GSI

GSI的全称是Global Secondary Index,它的作用就是提供不同的访问路径,或者说从不同维度来获取数据。

先来说说这个GSI是做什么用的。还是以前面的例子来说。

Student ID, Subject, Score, Date
--------------------------------
9901,       Math,     90,   2021-11-01
9901,       English,  80,   2021-11-02
9902,       Math,     92,   2021-11-01
9903,       Math,     63,   2021-11-01
9904,       Math,     90,   2021-11-01

在上面这个表中,主键是由Student ID(Partition Key)及Subject(Sort Key)组成的。之后可以针对这个两个字段进行查询。

但问题来了,如何通过日期字段查询?比如:返回所有在2021-11-01这天登录的考试成绩。这个时候,PK/SK组合就不够用了。只能通过创建一个GSI来达到这个目的。

创建一个GSI,就相当于又创建了一个当前表的镜像。只不过在镜像中,PK就是这个GSI。这就不难理解为什么可以通过GSI进行快速查询数据了。当然增加GSI的代价是cost。

在主表和GSI表之间的数据同步可能会有延迟,因此如果主表更新后,通过GSI可能查询不到最新数据。

建议在GSI的WCU应高于主表的WCU,这是因为所有对主表的更新操作,必然需要在GSI表中同步。推荐将GSI表设置为autoscaling。

LSI

LSI的全称是Local Secondary Index。

LSI和GSI的区别

GSI LSI
主键可以只包含分区键,也可以是分区键和索引键组合 只能是组合键(分区键+索引键)
分区键可以与基表分区键不同 分区键必须和主表相同
GSI没有大小限制 LSI有大小限制,比如10GB
可以在任何时候创建GSI 只能在创建表时创建LSI
通过GSI,可以跨分区进行查询 只能针对指定分区进行查询
对GSI,仅支持最终读取一致性(可能有延迟) 可选择强制一致性

使用LSI时的注意事项

  • 只有在创建表的时候可以定义LSI
  • 和GSI不同,使用LSI并不会产生额外费用
  • 每个表只能定义最多5个LSI(软性限制)
  • 一旦创建表,无法新增,删除,更改LSI
  • 使用LSI的查询会比针对主表的查询要快很多

DynamoDB数据库设计

DynamoDB的设计理念就是要在应用程序中保留尽可能少的表。如果设计得当,通常一个表就足够了

DynamoDB是schemaless的,因此在设计的时候纯粹是通过访问模式来确定其设计的。

由于经常会将不同类型的数据保存到同一个DynamoDB表中,因此,属性的名字和其值可能没有关系。在设计好的DynamoDB中,经常会使用非常通用的PK/SK作为属性名称,而不是StudentID, Subject等更为具体的名称。

PK SK Data Produced_at Sales_ID
PRODUCT_ID GB P Name 2021-10-02
ORDER_ID PRODUCT_ID CATEGORY_ID 100058
EMPLOYEE_ID MANAGER_ID Paul Bounce

Index overloading

将不同类别的数据保存在同一列中看似杂乱,但实则非常有用,这种方式叫做index overloading。

Sparse indexes稀疏索引

全局二级索引默认属于稀疏型。将全局二级索引设计为稀疏索引,可以配置低于基表的写入吞吐量,同时仍实现出色的性能。

关于一对多关系的设计

DynamoDB的设计是完全摒弃RDB中的规范化的,比如:

  1. 关于复杂属性(违反RDB的规范化)的设计:
  • 确保不需要直接针对复杂属性的某个部分进行访问
  • 注意DynamoDB对每个item的大小都有限制,为 < 400KB
  1. 关于数据的重复(违反RDB的规范化)
  • 通常在数据为只读时重复
  • 如果数据不经常更改,或者不会有过多份Copy

通常使用Composite Primary Key来实现一对多的关系。比如通过Partition Key来限定一组数据,然后再通过Sort Key来展现数据的不同方面。

有些时候需要Composite Sort Key,这在大于两级的item时候非常有用。比如Country#State#City#PostCode,之后就可以进行灵活的查询了。这个时候可以查询某个item以及所有下级item。

不适合单表设计的场景

  • 如果选择使用GraphQL,则只能选择多表。
  • 需要更多灵活性的时候

参考资料

https://www.youtube.com/watch?v=BnDKD_Zv0og
https://www.youtube.com/watch?v=AT2zkWoqQ5o
https://www.alexdebrie.com/posts/dynamodb-single-table/
https://www.trek10.com/blog/dynamodb-single-table-relational-modeling/


文章作者: 逻思
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 逻思 !
  目录