入门阅读约 4 分钟

DynamoDB 的 SQL 与 PartiQL 的局限

DynamoDB 是一个 NoSQL 键值存储,但它回答类 SQL 问题的能力比人们预想的要多——也远比人们期望的要少。这是一份诚实的地图:开箱即用你实际能得到多少 SQL-on-DynamoDB、它止步于何处,以及运行原生表层表达不了的 JOIN / GROUP BY / 聚合查询的少数几种办法。

能用 SQL 查询 DynamoDB 吗?

部分能。DynamoDB 内置了 ,一种兼容 SQL 的语言,可按键做 SELECT/INSERT/UPDATE/DELETE,所以 SELECT * FROM "Orders" WHERE OrderID = 100 是能用的。但它是 DynamoDB API 之上一个兼容 SQL 的表层,而不是一个 SQL 引擎——AWS 只支持它的一个_子集_,所以 JOINGROUP BYCOUNT(*) 都在外面。要用这些,你需要在其之上叠加一个引擎。

AWS 把 PartiQL 描述为 "一种兼容 SQL 的查询语言,用于在 Amazon DynamoDB 中 select、insert、update 和 delete 数据", 但同样直言不讳:"Amazon DynamoDB 支持 PartiQL 查询语言的一个_子集_。"你一伸手去用 JOINGROUP BYCOUNT(*),就已经越出了 PartiQL 能做的范围——完整的逐项功能对比参见 PartiQL 与 SQL

PartiQL:一个兼容 SQL 的表层,而非一个 SQL 引擎

PartiQL 把类 SQL 的语句映射到 SDK 所暴露的那些数据平面操作之上。带 相等条件的 SELECT 编译成一次 Query;不带的 SELECT 编译成一次 Scan。根据 AWS SELECT 参考

如果 WHERE 子句中没有提供带分区键的相等或 IN 条件,使用 SELECT 语句可能导致一次全表扫描。

所以支配 QueryScan 的那套访问模式规则依旧成立——PartiQL 只是把它们藏在了熟悉的语法后面。它不增加查询规划器、不增加连接、不增加基于集合的聚合。每条语句都收敛为一个原生操作:

一条不带分区键相等条件的 SELECT 会编译成一次全表 Scan。在 us-east-1 的按需模式下, 它对每一个被检查到的项按每 4 KB 0.5 RCU(最终一致读)计费 —— 一张由 2 KB 行组成的 500 MB 表,在任何 WHERE 过滤条件收窄结果集之前,大约就是 125,000 RCU。在 定价计算器里把 PartiQL 形状的读取按行费率算一遍。

你写的DynamoDB 运行的
SELECT … WHERE PK = …GetItemQuery
SELECT …(无 PK)Scan(读取整张表
INSERT INTO …PutItem
UPDATE … WHERE PK=… AND SK=…UpdateItem(单个项)
DELETE … WHERE PK=… AND SK=…DeleteItem(单个项)

如果某个操作无法归约为单个 Get/Query/Scan/Put/Update/Delete,PartiQL 就是表达不了它。下面的一切都是这一个事实的推论。

PartiQL 覆盖了什么

DynamoDB 的 PartiQL 支持四种 DML/查询语句:

  • SELECT —— 读取项(编译成 QueryScan
  • INSERT —— 添加一个项(PutItem
  • UPDATE —— 修改一个项(UpdateItem
  • DELETE —— 删除一个项(DeleteItem

它也支持 事务和批处理操作。一次格式良好的读取,会用相等或 IN 条件锁定分区键:

SELECT OrderID, Total
FROM "Orders"
WHERE OrderID IN [1, 2, 3] ORDER BY OrderID DESC

ORDER BY 是允许的,但 AWS 参考把排序键限制为"哈希键或排序键"——也就是分区键或 ,而非任意列。这就是 PartiQL 的 SELECT 所能接受的上限。想要可复制粘贴的语句,参见 PartiQL 示例

PartiQL 做不了什么

这些是开发者最常从"SQL"中期待的东西,而 PartiQL 一个都不支持

  • 没有 JOIN PartiQL SELECT 语法 是单个的 FROM {{table}}[.{{index}}]——一张表或一个索引,绝不是按键关联的两张表。这就是 单表设计 的取舍:你要提前围绕访问模式建模,因为查询层事后无法重塑数据。
  • 没有 GROUP BY 它不在语法里;没有子句可以对行分组。
  • 没有聚合函数。 PartiQL 函数参考 在"聚合函数"下恰好只列了一个函数:SIZE,它返回单个项某个属性的字节大小。跨行没有 COUNTSUMAVGMINMAX。AWS 说得很直白:"本列表未包含的任何 SQL 函数,DynamoDB 目前都不支持。"
  • 没有 LIKE、没有子查询、没有 UNION、没有窗口函数。 模式匹配用 contains / begins_with;其余的则根本没有对应物。

所以"上个月按客户统计的总营收"——在任何关系型数据库里都是一行 GROUP BY——在 PartiQL 里是表达不出来的。你得把数据扫出来,在应用代码里聚合。

要对 DynamoDB 数据获得真正的 JOIN / GROUP BY / 聚合行为,唯一的办法是用一个在其之上运行真正 SQL 引擎的工具。对于交互式、临时性的查询,有两个选择:Amazon Athena 的联合连接器,以及 DynoTable 的 SQL Workbench。(对于周期性分析,DynamoDB 到 Amazon Redshift 的 zero-ETL 集成也能运行 SQL 连接和聚合。)

如何借助 Amazon Athena 用真正的 SQL 查询 DynamoDB

AWS 自己对"对 DynamoDB 用真正的 SQL"的答案,是 Amazon Athena DynamoDB 连接器, 它"让 Amazon Athena 能与 DynamoDB 通信,从而你可以用 SQL 查询你的表"。因为 Athena 是一个完整的 SQL 引擎,这_确实_能给你 JOIN 和聚合——AWS 的操作指南标题就是 "使用 Athena 访问、查询并连接 Amazon DynamoDB 表"。

代价在于搭建和开销:

  • 它是一个你部署进自己账户的基于 Lambda 的联合连接器(通过 Athena 控制台或 Serverless Application Repository),经由 AWS Glue 处理 schema,并把结果溢写到一个 S3 存储桶 (连接器文档)。
  • 在底层,它仍然使用 DynamoDB 的 QueryScan API 操作。AWS 警告说"使用扫描的查询可能消耗大量读取容量单元(RCU)",所以一条针对大表的分析查询会读取——并计量——大量的项 (连接器开销)。用 项大小计算器 来估算一条扫描密集型查询的开销。
  • INSERT INTO 这样的写入操作,连接器不支持。

Athena 是周期性分析和 BI 仪表盘的对路工具。对于日常"我就想连接两张表、瞄一眼结果"的场景,它太重了——那正是下一节要补上的空缺。

DynoTable 的 SQL Workbench:在 DynamoDB 访问模式规则之内的 SQL

DynoTable 的 SQL Workbench 从一个桌面客户端对你实时的 DynamoDB 表运行真正的 SQL——JOINGROUP BYCOUNT/SUM/AVG——无需搭建 Lambda、Glue 或 S3。它通过 DynamoDB 真实的 Query/Scan 运行时物化这些行,然后在你的桌面上本地对它们运行单条 SELECT

-- Runs in the DynoTable Workbench (NOT in PartiQL):
SELECT c.country, COUNT(*) AS orders, SUM(o.total) AS revenue
FROM orders o
INNER JOIN customers c ON o.customerId = c.PK
GROUP BY c.country
ORDER BY revenue DESC

"在 DynamoDB 访问模式规则之内"这部分很重要。Workbench 并不假装 DynamoDB 是 Postgres——它在底层仍然通过 Query/Scan 读取,所以你始终清楚每条查询的开销,而且它强制执行 DynamoDB 的访问模型,而非把它藏起来:

  • 仅支持 INNER JOINLEFT JOIN——ON 的目标属性必须是分区键或 GSI 分区键。不支持 RIGHT / FULL / CROSS / 逗号连接。
  • 暂不支持自连接、子查询、派生表、窗口函数。
  • 连接与投影作用于标量属性。

如果你只是需要为原始 API 拼装条件和键表达式——而非一条完整的 SQL 语句——DynamoDB Expression Builder 会生成正确的 FilterExpression / KeyConditionExpression,完全不必碰 PartiQL 表层。

如果你的目标是一个用于探索、调试和分析表的 DynamoDB SQL 客户端,Workbench 就填补了这个空缺——而 DynoTable 的其余部分是围绕它的一个完整 DynamoDB GUI

试用 DynoTable,对你自己的表运行真正的 SQL。

常见问题

能在 DynamoDB 上运行 SQL 吗? 你可以运行 PartiQL,一个兼容 SQL 的子集(按键做 SELECT/INSERT/UPDATE/DELETE)。要用 JOIN、GROUP BY 和聚合,你需要在其之上叠加一个 SQL 引擎:Amazon Athena DynamoDB 连接器,或 DynoTable 的 SQL Workbench——一种单条 SELECT 的方言,支持 INNER/LEFT JOIN,没有 CTE、UNION 或子查询。

DynamoDB PartiQL 支持 JOIN 吗? 不支持。PartiQL 的 SELECT 语法只有单个 FROM 表或索引,没有连接语法。连接需要在 DynamoDB 之上叠加一个引擎。

PartiQL 支持 GROUP BY 或像 COUNT、SUM 这样的聚合吗? 不支持。没有 GROUP BY 子句,唯一的"聚合"函数是 SIZE(单个项某个属性的字节大小)。跨行的 COUNTSUMAVGMINMAX 都不支持。

DynamoDB 是 SQL 还是 NoSQL? NoSQL——一个键值与文档存储。PartiQL 在其上加了一层兼容 SQL 的查询语言,但 DynamoDB 没有关系引擎、连接或聚合。

PartiQL 适合临时查询吗? 对于按键的查找,适合。对于分析性的临时查询(计数、汇总、连接),不适合——PartiQL 表达不了它们,而不加约束的 SELECT 会悄无声息地变成全表扫描。

有能处理 JOIN 和 GROUP BY 的 DynamoDB SQL 客户端吗? 有——DynoTable 的 SQL Workbench 能从桌面对实时的表运行 JOIN/GROUP BY/聚合,Amazon Athena 则通过一个你部署在自己 AWS 账户里的联合连接器来做到。

更新于