入门阅读约 3 分钟

何时该用 DynamoDB(以及何时不该)

对于它所擅长的工作负载,DynamoDB 是一个出色的数据库;对于其余的,它令人沮丧。决定性的问题 不是「它能不能扩展到 web 级?」—— 而是「我是否提前知道我的访问模式,而且它们是基于键的吗?」 把这点搞对,DynamoDB 会在任意规模下给你个位数毫秒的读取;搞错了,你就会永远跟它缺乏 join 和 即席查询的事实较劲。

何时该使用 DynamoDB?

当你的访问模式已知、基于键且吞吐量高,并且希望在任意规模下获得可预测的个位数毫秒延迟、无需管理任何服务器时,选择 DynamoDB。如果你需要即席查询、复杂 join 或全数据集分析,或者数据量小且查询形态还在不断变化,则应避开它。

  • 在以下情况用 DynamoDB:你的访问模式已知、基于键、且高流量 —— 而你想要在任意规模下都 可预测的延迟,且无服务器要管。
  • 在以下情况避开它:你需要即席查询、丰富的 join,或对整个数据集做分析;或者数据量小而查询 形态又不断变化。
  • 核心取舍:DynamoDB 要你提前为查询做设计;作为回报,它在你增长时永不变慢。
  • 它不是换了套语法的关系型数据库 —— 把它当关系型来建模是痛苦的头号来源。

偏向 DynamoDB 的信号

当下面这些大多成立时,DynamoDB 大放异彩:

  • 你提前知道访问模式。 你能列出应用发起的确切查询(「按 id 取一个用户」、「列出某用户的 订单,最新优先」),而且它们不会随意变。DynamoDB 就是围绕这些查询建模的。
  • 访问是基于键的。 你按已知的分区键查找项,而不是为任意属性组合做扫描。
  • 规模与可预测延迟很重要。 无论表里装的是一千个项还是十亿个项,DynamoDB 都交付 稳定的个位数毫秒 性能。
  • 你想要零运维负担。 没有实例、没有故障转移、没有 vacuum —— 它完全托管,并能按需缩容 到零。
  • 写入吞吐量高而尖峰。 事件日志、IoT 遥测、会话/购物车状态、排行榜 —— 带有清晰键的、 以追加为主的工作负载。

反对它的信号

在以下情况转向关系型数据库(或搜索/分析引擎):

  • 你的查询是即席的。 分析师按任意列切分数据,或需求每周都变。这时 SQL 的灵活性胜出; DynamoDB 则需要为每个模式建一个新索引。
  • 你需要在整个数据集上做真正的 join 和聚合。 报表、商业智能、「按地区按月汇总收入」—— 那是 OLAP/关系型的活儿。(对着实时表提一次性问题则是另一回事——DynoTable 的 SQL Workbench 会在客户端对 DynamoDB 运行 JOINGROUP BY 和聚合;真正该放到别处的是常态化的 BI 工作负载。)
  • 数据集小且低流量。 一个安静的管理后台里几千行数据,从 DynamoDB 的规模里得不到好处, 反而失去了 SQL 的便利。
  • 你还无法预测访问模式。 早期产品仍在摸索形态?一个你能自由重新查询的关系型 schema 在 模式稳定下来之前更宽容。
否,即席 / 多变否,小且安静新的工作负载访问模式已知且基于键?关系型数据库需要跨数据集的 join / 分析?高规模或尖峰写入?DynamoDB

DynamoDB 与其他数据库的对比

「该用 DynamoDB 还是 X?」通常只是同一个问题换了身衣服:X 能不能让我把访问模式的决定往后推,而我要为此付出什么代价? DynamoDB 正是那个拒绝让你往后推的选项。下面每一组对比都围绕这一个取舍展开,而不是功能清单。

关系型:PostgreSQL、RDS 与 Aurora

这才是真正的分岔口,也是最多团队走错的那个。关系型数据库允许你在拿到数据之后再写查询。DynamoDB 不行 —— 表的形态在第一个项写入之前,就已经被查询决定了。

当查询形态还在变动、当你需要跨整个数据集做 join 或聚合、或者数据量小到规模根本不是你的问题时,选关系型。当访问模式已经定型且基于键,而你希望它在十亿个项时的代价和一千个项时一样时,选 DynamoDB。

RDS 和 Aurora 并不改变这笔账 —— 它们是托管的关系型引擎,因此既继承了 SQL 的灵活性,也继承了它的扩展模型。它们改变的是运维层面的对比:有了 Aurora Serverless,DynamoDB「没有服务器要管」这个论点就弱了很多,决策于是干净地落回访问模式上。Aurora 扩展的是计算;DynamoDB 直接取消了这个概念。

文档型:MongoDB 与 DocumentDB

两者都存储类 JSON 的文档,所以远看像是可以和 DynamoDB 互换。其实不然。MongoDB 能为任意字段建索引并对其运行即席查询;DynamoDB 给你的只有分区键、排序键,以及你事先声明好的那些索引。

这让 MongoDB 更适合仍在演进的查询形态,而 DynamoDB 更适合高流量下已知的查询形态。DocumentDB 处在同一条线的 AWS 一侧 —— 它兼容 MongoDB API,所以把它看作「MongoDB 的灵活性,加上 AWS 的运维模型」,并且完全按上面那条「灵活性对可预测性」的轴来和 DynamoDB 比较。

宽列型:Cassandra

Cassandra 是 DynamoDB 在架构上最近的亲戚:分区键、聚簇键,以及同样那条硬道理 —— 糟糕的分区键是一个你无法靠加索引绕开的设计缺陷。如果你在两者之间做选择,决定因素很少是数据模型 —— 而是谁来运维它,以及你怎么付钱。Cassandra 要你自己运维(或者买托管服务);DynamoDB 则是你直接消费。Amazon Keyspaces 是托管版 Cassandra 的折中地带。

正因为模型如此接近,本站的建模指导大多可以迁移过去:单表设计中关于分区键和访问模式的那套推理,几乎可以逐行套用到 Cassandra 上。

内存型:Redis

这并不是一个真正的二选一。Redis 以内存为先,为亚毫秒级访问那些丢了也能重建的数据而优化;DynamoDB 则默认持久。生产环境里常见的答案是两者都要 —— DynamoDB 作为权威数据源,Redis(或者 DAX,也就是 DynamoDB 自己的读穿透缓存)挡在热键前面。

只有当数据确实是短暂的时候,才单独选 Redis:限流计数器、短生命周期的会话、可以重新算出来的排行榜。

搜索型:Elasticsearch 与 OpenSearch

这同样不是一个二选一,而且理由比 Redis 那节更直白:DynamoDB 根本没有全文搜索。 Query 只能按键相等以及一小组排序键条件来匹配。带 FilterExpressionScan 会读取每一个项,然后把其中大部分丢掉 —— 那是一次外挂了筛选条件的全表遍历,不是搜索,而且你付的是读取的项的钱,而不是返回的项的钱。这里没有相关性排序,没有分析器,没有模糊匹配,也没有分面。

所以问题从来不是「DynamoDB 还是搜索引擎」,而是「这个工作负载需不需要搜索?如果需要,谁来喂这个索引?」标准形态是两者都要:DynamoDB 作为权威数据源,旁边跟一个搜索集群,由 DynamoDB Streams 把每一次变更送进索引。这买来了真正的搜索,代价是你要多运维一套系统,还要接受一个与表最终一致的索引。

OpenSearch 和 Elasticsearch 是同一个决定。 OpenSearch 是 AWS 从 Elasticsearch 分叉出来的版本,2021 年因 Elastic 的许可证变更在 7.10 处分道扬镳,此后两者渐行渐远。但这些差异都不影响这里的问题 —— 对于「搜索该不该放在 DynamoDB 之外」,它们的表现完全一致。在两者之间做选择,看的是许可、托管方式和你想运维哪个托管服务,而不是任何和 DynamoDB 有关的因素。

只有当搜索确实就是产品本身时,才把搜索引擎当作主存储 —— 日志分析,或者主要访问模式就是自由文本的商品目录。即便如此,大多数团队仍会在它后面保留一个持久化存储,因为搜索索引是一个派生视图,你必须能够重建它。

数据模型对比掩盖掉的成本维度

上面每一组对比谈的都是数据模型,但账单上的意外通常是结构性的:关系型引擎按你预置的容量计费,DynamoDB 按你执行的操作计费。这让 DynamoDB 对尖峰和空闲的工作负载很便宜,对持续扫描则很昂贵 —— 同一个工作负载可以在一个引擎上大获全胜,在另一个上惨败,中间连一行代码都不用改。

人们漏掉的那个倍数是索引。在关系型引擎上,多一个索引的代价是存储加上一点写入延迟;在 DynamoDB 上,每一个二级索引都意味着对投影的属性做一次完整的额外写入。我们在索引指南里按三种写入量把这笔账算了出来 —— 一个 GSI 让写入账单翻倍,两个则变成三倍。在你倒向任何一边之前,先用定价计算器给你真实的读/写组合建个模。

在投入前算清成本

DynamoDB 的定价跟随读取、写入和存储 —— 而非实例小时 —— 所以它对尖峰和无服务器工作负载很便宜, 对持续的大量扫描则可能昂贵。在投入之前用 DynamoDB 定价计算器给你真实的读/写组合建模;一个技术上 看似合适的工作负载,在成本上也应该算得过来。

一旦你判定它合适

工作就转向建模了。DynamoDB 奖励那些把表围绕查询来设计的做法 —— 见如何在 DynamoDB 中建模数据单表设计——以及明确的 何时不该采用单表设计

在 DynoTable 的条目网格中浏览一张有数据的 DynamoDB 表。
在 DynoTable 的条目网格中浏览一张有数据的 DynamoDB 表。

陷阱与后续步骤

  • 别把 DynamoDB 当关系型数据库来建模 —— 在读取时 join 的规范化表,正是它惩罚得最狠的 反模式。
  • 别拿它做分析 —— 把它和一个分析存储搭配(或导出到一个),用于报表,而不是去扫描。
  • 不确定访问模式?再等等。 在你了解自己的查询之前就采用 DynamoDB,是选了那个唯一要求你 必须先了解查询的数据库。
  • 相关: Query 与 Scan 展示了「基于键的访问」究竟为你买到了什么。

想在把应用押上去之前先探索一张 DynamoDB 表吗?下载 DynoTable,直接连到你的数据——它的 SQL Workbench 能运行那些 DynamoDB 自己不肯跑的临时 JOIN 和聚合。

更新于