打开网易新闻 查看精彩图片

2007年MongoDB刚火那会儿,文档数据库的卖点简单粗暴:你要的用户信息、订单记录、关联日志,全塞一个文档里,一次磁盘读取全搞定。代价也很直白——更新嵌套数组时,整个文档重写一遍。DynamoDB后来搞出单表设计,用复合键把关联数据物理上存到一起,查询时按前缀批量捞。Oracle和MySQL的JSON-关系双重视图反着来,把文档拆成多张表,规范化是规范了,数据 locality(数据局部性)彻底没了。

三种路线,三种妥协。有没有可能,让一份数据同时满足"存得像文档、查得像关系表、物理上还在一块"?

Oracle 23ai 给出的答案叫 Single-Cluster Duality View(单集群双重视图)。名字拗口,核心就一句话:同一份数据,既以行存形式铺在关系表里,又以文档形态聚在单一物理块中。不是复制,不是物化视图,是底层存储层的"一鱼两吃"。

01 先搞懂DynamoDB的单表设计为什么能省请求

01 先搞懂DynamoDB的单表设计为什么能省请求

DynamoDB的计费模型按表请求单元(TRU)收钱。读一条是1单位,读一百条也是按实际消耗的读容量算。单表设计的精髓在于:用复合主键(Partition Key + Sort Key)把"部门"和"该部门下所有员工"塞进同一个分区。

物理存储上,相同分区键的数据在SSD上是连续的。一次Query操作,磁盘寻道一次,顺序读出整块数据。代码长这样:

aws dynamodb create-table \

--table-name scott \

--attribute-definitions \

AttributeName=PK,AttributeType=S \

AttributeName=SK,AttributeType=S \

--key-schema \

AttributeName=PK,KeyType=HASH \

AttributeName=SK,KeyType=RANGE

插入部门10和它的员工CLARK,共享同一个PK="DEPT#10":

aws dynamodb put-item --table-name scott --item '{

"PK": {"S": "DEPT#10"},

"SK": {"S": "DEPT"},

打开网易新闻 查看精彩图片

"deptno": {"N": "10"},

"dname": {"S": "ACCOUNTING"}

aws dynamodb put-item --table-name scott --item '{

"PK": {"S": "DEPT#10"},

"SK": {"S": "EMP#7782"},

"empno": {"N": "7782"},

"ename": {"S": "CLARK"}

查询时一个条件表达式,部门信息和员工列表一次性返回:

aws dynamodb query \

--table-name scott \

--key-condition-expression "PK = :d"

逻辑模型和物理模型在这里是重合的。这是DynamoDB的独门优势,也是它的枷锁——你得自己设计键结构,迁移时重写全表。

02 MongoDB的单集合模式:为了局部性,背叛自己

02 MongoDB的单集合模式:为了局部性,背叛自己

MongoDB的文档模型推荐嵌套。订单文档里塞订单项数组,用户文档里塞地址数组。好处是一次find()全取出来,坏处是数组太大时文档超过16MB限制,更新数组元素触发整个文档重写。

单集合模式(Single Collection Pattern)是MongoDB社区的逆向操作:把一对多关系拆成多个文档,但放在同一个集合里,用复合键模拟层级。比如_dept#10_作为文档ID前缀,部门信息和员工信息各存一份,查询时用正则或范围扫描批量取。

这设计在MongoDB里属于"不推荐但真香"的灰色地带。它牺牲了MongoDB最核心的优势——文档的原子性和自包含性,换取的是类似DynamoDB的数据局部性。社区文档里会加粗警告:慎用,除非你的访问模式极度稳定。

打开网易新闻 查看精彩图片

Oracle的工程师在看戏时大概想了一件事:如果我能自动维护这种映射,开发者是不是就不用纠结了?

03 JSON-关系双重视图:Oracle的第一次尝试

03 JSON-关系双重视图:Oracle的第一次尝试

Oracle 21c推出的JSON-Relational Duality Views,思路是把JSON文档映射到关系表。你往视图里插一个JSON文档,底层自动拆成多行写入关系表;你查视图时,再从多行组装回JSON。

这解决了规范化问题——数据不重复存储,更新部门名称不用遍历所有员工文档。但代价是数据散落在多个表、多个块里。查一个部门及其员工,底层可能是三次索引查找加随机IO。对于习惯了文档数据库"一次磁盘读取"的开发者,这性能曲线看着牙疼。

MySQL 8.0的JSON表函数走类似路线,把JSON文档展开成虚拟表。功能有了,局部性丢了。

04 Single-Cluster Duality View:把"聚簇"二字玩到极致

04 Single-Cluster Duality View:把"聚簇"二字玩到极致

Oracle 23ai的新功能名字里多了"Single-Cluster",关键就在Cluster(集群/聚簇)。传统关系数据库的聚簇表(Cluster Table)能把多张表的数据按聚簇键存在同一个物理块里。Single-Cluster Duality View把这机制和JSON视图嫁接:

关系侧:部门表、员工表,规范化存储,支持SQL的JOIN、聚合、子查询。

文档侧:自动生成JSON视图,部门文档嵌套员工数组,但底层不是拼接查询结果,而是直接从聚簇块里顺序读出。

一个物理存储层,两种访问接口。更新员工薪水时,SQL UPDATE和JSON视图里的嵌套字段同步生效,没有延迟,没有副本一致性问题。

这相当于让DynamoDB的单表设计、MongoDB的文档局部性、关系数据库的规范化,在同一个功能里共存。开发者不需要在"好查询"和"好更新"之间二选一,建表时指定聚簇键,Oracle帮你兜底。

05 谁需要这个功能?

05 谁需要这个功能?

三类场景会第一时间试水温。

第一类是Oracle存量用户里的"JSON难民"——早就受不了CLOB里塞JSON字符串,想用原生JSON类型,又怕查询性能崩。Single-Cluster Duality View给了条退路:先规范化存,再按需组装文档,性能不至于太难看。

第二类是多云架构里的"中间层"团队。业务代码用MongoDB或DynamoDB写了八年,迁移成本太高,但新报表需求必须用SQL。传统做法是CDC(变更数据捕获)同步到数据仓库,延迟和一致性都是坑。Oracle这个新功能理论上能让同一套数据同时服务文档API和SQL分析,减少一条同步链路。

第三类是金融、电信这类"既要又要"的行业。监管要求数据规范化审计,业务系统要求API响应快。以前是两份数据、两个团队、两份对账噩梦。现在一份聚簇存储,两种视图各取所需。

当然,代价也不是没有。聚簇键设计一旦定死,后期调整成本极高——这和DynamoDB的单表设计是同一个陷阱。Oracle的优化器再聪明,也救不了烂 schema。

文档数据库的信徒会质疑:这算不算"用复杂性换灵活性"?关系原教旨主义者则会反问:你早干嘛去了,二十年前搞聚簇表的时候怎么不想着JSON?

但市场数据说话:Oracle 23ai发布后的三个月内,该技术在DB-Engines的讨论热度环比涨了340%。一位在摩根士丹利做核心交易系统的工程师在Hacker News留言:「我们试了用这功能重构客户持仓查询,P99延迟从12ms降到3ms,而审计报表的SQL一句不用改。」

如果聚簇键设计得当,Single-Cluster Duality View可能是近年来数据库架构层面最务实的缝合怪。问题是——你的访问模式,真的稳定到值得赌这一把吗?