Microsoft DocumentDB 深度解析:基于 PostgreSQL 的开源 MongoDB 兼容引擎——从 BSON 原生存储到协议网关、从性能基准到生产部署的完整技术指南(2026)
作者按:2024 年微软悄悄做了一个决定——把支撑 Azure Cosmos DB for MongoDB 的引擎开源,取名 DocumentDB。它不是一个简单的 MongoDB 包装层,而是把 BSON 作为一等公民数据类型直接嵌入 PostgreSQL 内核,用三个 PostgreSQL 扩展(pg_documentdb_core、pg_documentdb、pg_documentdb_gw)实现了完整的文档数据库能力。本文从架构到代码,从基准测试到生产部署,给你一个完整的 DocumentDB 技术全景。
目录
- 背景与动机:为什么世界需要另一个 MongoDB 兼容层?
- DocumentDB 架构全景:三个扩展的分工与协作
- 深入 pg_documentdb_core:BSON 在 PostgreSQL 中的原生存储
- 深入 pg_documentdb:CRUD API 与查询引擎
- 深入 pg_documentdb_gw:MongoDB 协议翻译层
- 快速上手:Docker 一键部署与 Mongo Shell 连接
- 代码实战:用官方 MongoDB 驱动操作 DocumentDB
- 高级查询:全文搜索、地理空间、向量搜索
- 性能基准:DocumentDB vs MongoDB vs PostgreSQL JSONB
- 迁移指南:从 MongoDB 到 DocumentDB
- 生产部署:Kubernetes Operator 与高可用架构
- 开源治理:Linux Foundation 接管与 NoSQL 标准愿景
- 总结与展望
1. 背景与动机:为什么世界需要另一个 MongoDB 兼容层?
1.1 NoSQL 世界的碎片化困境
MongoDB 自从 2009 年开源以来,已经成为文档数据库的事实标准。但随着 MongoDB Inc. 在 2018 年将许可证从 AGPL 切换到 SSPL(Server Side Public License),整个生态开始出现裂痕。SSPL 要求任何提供 MongoDB 即服务的厂商必须开源其整个服务栈,这直接导致了:
- AWS 开发了 Amazon DocumentDB(MongoDB 兼容,但并非基于 MongoDB 代码)
- Azure 推出了 Azure Cosmos DB for MongoDB(vCore 和 RU 两种模式)
- 阿里云、腾讯云 也各自推出了兼容版
这些"兼容层"各自为战,协议实现程度不一,驱动兼容性参差不齐,用户一旦选型就被锁定。
1.2 微软的破局思路
微软的 Azure Cosmos DB for MongoDB(vCore 模式)需要一个新的引擎架构:既要完全兼容 MongoDB 驱动协议,又要能利用 PostgreSQL 成熟的生态(备份、HA、扩展)。
核心设计目标:
PostgreSQL 的可靠性 + MongoDB 的易用性 = DocumentDB
具体来说:
- 把 BSON 作为 PostgreSQL 的原生数据类型(就像
integer、text一样) - 在 PostgreSQL 扩展内实现 MongoDB 查询语言(MQL)的解析和执行
- 提供一个协议网关,让现有 MongoDB 驱动无需修改就能连接
1.3 为什么选择 PostgreSQL 作为底座?
| 考量维度 | PostgreSQL 优势 |
|---|---|
| 成熟度 | 30+ 年历史,ACID 实现完整,CRASH SAFE |
| 扩展性 | Extension 机制完善,支持自定义数据类型、操作符、索引 |
| 生态 | 丰富的工具链(pg_dump、pg_rewind、Patroni、pgBackRest) |
| 性能 | 10+ 版本性能飞跃,JIT、并行查询、覆盖索引均已支持 |
| 开源许可 | PostgreSQL License(类似 MIT),无商业风险 |
2. DocumentDB 架构全景:三个扩展的分工与协作
DocumentDB 不是单体应用,而是 三个 PostgreSQL 扩展 + 一个网关进程 的组合:
┌─────────────────────────────────────────────────────────────┐
│ MongoDB Driver │
│ (mongo-go-driver / pymongo / mongoose) │
└──────────────────────┬──────────────────────────────────────┘
│ MongoDB Wire Protocol (27017)
▼
┌─────────────────────────────────────────────────────────────┐
│ pg_documentdb_gw (Gateway) │
│ 协议翻译:MQL → PostgreSQL SQL │
└──────────────────────┬──────────────────────────────────────┘
│ libpq / SQL
▼
┌─────────────────────────────────────────────────────────────┐
│ PostgreSQL (带扩展) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌────────────┐ │
│ │ pg_documentdb_gw│ │ pg_documentdb │ │pg_documentdb│ │
│ │ _gw (函数) │ │ _core (类型) │ │_extended_rum│ │
│ └─────────────────┘ └─────────────────┘ └────────────┘ │
│ │
│ BSON 数据类型 │ CRUD 函数 │ 索引支持 │ 查询执行器 │
└─────────────────────────────────────────────────────────────┘
2.1 pg_documentdb_core:BSON 数据类型扩展
这是整个项目的基石。它做了几件关键的事:
① BSON 类型的 PostgreSQL 内部实现
// pg_documentdb_core 内部:BSON 在 PostgreSQL 中的存储结构
typedef struct BsonData {
int32 raw_size; // BSON 原始字节长度
uint8 raw_data[FLEXIBLE_ARRAY_MEMBER]; // BSON 二进制数据
} BsonData;
// 注册为 PostgreSQL 原生类型
PG_FUNCTION_INFO_V1(bson_in);
PG_FUNCTION_INFO_V1(bson_out);
// 输入函数:从 Hex 字符串解析 BSON
Datum bson_in(PG_FUNCTION_ARGS) {
char *input = PG_GETARG_CSTRING(0);
BsonData *result = (BsonData *)palloc(VARHDRSZ + input_len);
// ... 解析 BSON Hex -> 二进制 ...
PG_RETURN_BSON(result);
}
② BSON 路径访问操作符
模仿 MongoDB 的 doc.field 访问方式,在 PostgreSQL 中实现:
-- 安装扩展后可以直接用 -> 操作符访问 BSON 字段
SELECT data->'name' FROM documents; -- 等价于 MongoDB 的 doc.name
SELECT data->'address'->'city' FROM documents; -- 嵌套访问
SELECT data['tags'][0] FROM documents; -- 数组访问
③ BSON 比较与排序规则
MongoDB 的 BSON 比较语义与 PostgreSQL 不同(比如 null 的处理、类型优先级),pg_documentdb_core 实现了完整的 BSON 比较规则:
// BSON 类型优先级(MongoDB 规范)
// 1. Null
// 2. Numbers (int32/int64/double 按数值比较)
// 3. String
// 4. Object
// 5. Array
// 6. ...
2.2 pg_documentdb:CRUD API 扩展
这一层提供了完整的 MongoDB 操作 API,每个 MongoDB 命令都对应一个或多个 PostgreSQL 函数:
| MongoDB 命令 | PostgreSQL 函数 |
|---|---|
find() | documentdb.find(collection, filter, projection) |
insertOne() | documentdb.insert_one(collection, document) |
updateOne() | documentdb.update_one(collection, filter, update) |
deleteMany() | documentdb.delete_many(collection, filter) |
aggregate() | documentdb.aggregate(collection, pipeline) |
createIndex() | documentdb.create_index(collection, keys, options) |
关键实现细节:aggregate() 管道的每个阶段都被翻译成一个 PostgreSQL CTE(Common Table Expression):
-- MongoDB: db.orders.aggregate([{$match: {status: "shipped"}}, {$group: {_id: "$userId", total: {$sum: "$amount"}}}])
-- 翻译成 PostgreSQL:
WITH matched AS (
SELECT data FROM orders
WHERE (data->'status')::text = '"shipped"'
),
grouped AS (
SELECT
(data->'userId')::text AS user_id,
SUM((data->'amount')::numeric) AS total
FROM matched
GROUP BY (data->'userId')
)
SELECT * FROM grouped;
2.3 pg_documentdb_gw:协议网关
这是一个独立的进程(也可以以扩展函数形式运行),负责:
- 监听 MongoDB Wire Protocol 端口(默认 27017)
- 解析 MongoDB 客户端发来的 BSON 格式请求
- 翻译成对应的
pg_documentdb函数调用 - 把结果再编码成 MongoDB 响应格式返回
协议兼容性矩阵(截至 2026 年 7 月):
| 功能 | 支持状态 | 备注 |
|---|---|---|
| CRUD 基本操作 | ✅ 完整支持 | insert/find/update/delete |
| 聚合管道 | ✅ 大部分支持 | $lookup 跨表待完善 |
| 事务 | ✅ 支持 | 基于 PostgreSQL SAVEPOINT |
| Change Stream | 🚧 开发中 | 基于 PostgreSQL NOTIFY/LISTEN |
| 分片 | ❌ 不支持 | 用 PostgreSQL 分区表替代 |
| MongoDB 版本兼容 | 4.0+ | 驱动兼容 4.0+ |
3. 深入 pg_documentdb_core:BSON 在 PostgreSQL 中的原生存储
3.1 BSON 的存储格式设计
PostgreSQL 的 Bytea 类型本来就能存二进制数据,为什么还要自定义 BSON 类型?
原因一:操作符重载
自定义类型可以定义专属的操作符,让查询写起来像 MongoDB:
-- 用 Bytea 存 BSON(错误方式)
SELECT * FROM docs WHERE data::text LIKE '%"name":"Alice"%'; -- 慢,不支持索引
-- 用 BSON 类型(正确方式)
SELECT * FROM docs WHERE data->'name' = '"Alice"'::bson; -- 快,可用索引
原因二:存储优化
pg_documentdb_core 对 BSON 做了对齐优化,让字段访问不需要每次都解析整个文档:
// BSON 在 PostgreSQL 页面中的存储布局
// ┌─────────────────────────────────────────────┐
// │ varlena header (4 bytes) │
// ├─────────────────────────────────────────────┤
// │ bson_flags (4 bytes) - 缓存的元信息 │
// │ bit 0: has_object │
// │ bit 1: has_array │
// │ bit 2: cached_field_count │
// ├─────────────────────────────────────────────┤
// │ raw BSON data (variable) │
// │ [int32 size][byte type][cstring fieldname]...│
// └─────────────────────────────────────────────┘
3.2 BSON 索引实现
MongoDB 的索引能力是其核心竞争力之一。DocumentDB 通过 PostgreSQL 的 GIN(Generalized Inverted Index)和自定义 RUM 索引实现类似能力:
① 单字段索引
-- 等价于 MongoDB: db.users.createIndex({email: 1})
CREATE INDEX idx_users_email ON users
USING BTREE ((data->'email'));
② 复合索引
-- 等价于 MongoDB: db.orders.createIndex({userId: 1, createdAt: -1})
CREATE INDEX idx_orders_user_created ON orders
USING BTREE ((data->'userId'), (data->'createdAt') DESC);
③ 数组索引(GIN)
-- 等价于 MongoDB: db.articles.createIndex({tags: 1})
CREATE INDEX idx_articles_tags ON articles
USING GIN ((data->'tags'));
-- 支持查询:{tags: "postgres"} 命中 GIN 索引
④ 全文搜索索引(RUM)
pg_documentdb_extended_rum 扩展基于 PostgreSQL RUM 索引做了定制,支持 MongoDB 的全文搜索 API:
-- 等价于 MongoDB: db.posts.createIndex({content: "text"})
CREATE INDEX idx_posts_content_txt ON posts
USING RUM ((data->'content'), rum_tsvector_ops);
3.3 BSON 的函数库
pg_documentdb_core 提供了一整套 BSON 操作函数:
-- 字段访问
SELECT bson_get_field(data, 'name') FROM users;
-- 类型判断
SELECT bson_typeof(data->'age'); -- 返回 "int32" | "int64" | "double" | ...
-- BSON 构造
SELECT bson_build_object('name', 'Alice', 'age', 30);
-- BSON 解析(展开为行)
SELECT * FROM bson_unnest('{"name": "Alice", "age": 30}'::bson);
-- 结果:
-- key | value
-- -----+--------
-- name | "Alice"
-- age | 30
4. 深入 pg_documentdb:CRUD API 与查询引擎
4.1 插入操作的事务保证
DocumentDB 的插入操作直接利用 PostgreSQL 的事务机制:
# 使用 pymongo 驱动(无需修改)
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["mydb"]
users = db["users"]
# insert_one 在 DocumentDB 中的执行流程:
# 1. 驱动发送 MongoDB Wire Protocol 的 OP_INSERT 消息
# 2. pg_documentdb_gw 解析,生成 SQL:
# SELECT documentdb.insert_one('users', '{"name": "Alice", "age": 30}'::bson);
# 3. pg_documentdb.insert_one() 函数执行:
# a. 分配 _id(如果未提供)
# b. 写入 WAL(Write-Ahead Log)
# c. 返回结果
result = users.insert_one({"name": "Alice", "age": 30})
print(result.inserted_id) # ObjectId("...")
4.2 查询引擎:MQL 到 SQL 的完整翻译
MongoDB 查询语言(MQL)的很多特性需要巧妙的翻译策略:
① $or 查询
# MQL
users.find({"$or": [{"age": {"$gte": 18}}, {"name": "Alice"}]})
翻译成 PostgreSQL:
SELECT data FROM users WHERE
(data->'age')::int >= 18
OR (data->'name')::text = '"Alice"';
-- 如果两个字段都有索引,PostgreSQL 会用 BitmapOr 合并索引扫描
② $elemMatch 数组查询
# MQL:查找 tags 数组中同时有 "postgres" 和 "performance" 的文档
articles.find({"tags": {"$all": ["postgres", "performance"]}})
翻译成 PostgreSQL(使用 GIN 索引):
SELECT data FROM articles WHERE
(data->'tags') @> '["postgres", "performance"]'::bson;
-- @> 是 GIN 索引支持的操作符,表示"包含"
③ $lookup 关联查询
# MQL:订单关联用户
db.orders.aggregate([
{"$lookup": {
"from": "users",
"localField": "userId",
"foreignField": "_id",
"as": "user"
}}
])
翻译成 PostgreSQL(使用 CTE + JOIN):
WITH lookup AS (
SELECT
o.data as order_data,
u.data as user_data
FROM orders o
LEFT JOIN users u ON (o.data->'userId')::text = (u.data->'_id')::text
)
SELECT
jsonb_set(
order_data::jsonb,
'{user}',
COALESCE(jsonb_agg(user_data), '[]'::jsonb)
)
FROM lookup
GROUP BY order_data;
4.3 更新操作的原子性
MongoDB 的更新操作支持原子性字段修改,DocumentDB 通过 PostgreSQL 的 UPDATE ... RETURNING 实现:
# MQL:$inc 原子递增
users.update_one(
{"_id": ObjectId("...")},
{"$inc": {"loginCount": 1}}
)
翻译成 PostgreSQL:
UPDATE users
SET data = documentdb.bson_inc(data, 'loginCount', 1)
WHERE (data->'_id')::text = '"..."'
RETURNING data->'loginCount';
5. 深入 pg_documentdb_gw:MongoDB 协议翻译层
5.1 MongoDB Wire Protocol 速览
MongoDB 的通信协议是二进制格式,基于消息头 + 操作码的设计:
┌────────────────────────────────────────────────┐
│ Message Header (16 bytes) │
│ ├─ messageLength (int32) │
│ ├─ requestId (int32) │
│ ├─ responseTo (int32) │
│ └─ opCode (int32) │
├────────────────────────────────────────────────┤
│ Body (variable, depends on opCode) │
│ OP_QUERY (2004) - 查询 │
│ OP_INSERT (2002) - 插入 │
│ OP_UPDATE (2001) - 更新 │
│ OP_DELETE (2006) - 删除 │
│ OP_MSG (2013) - 新版通用格式(MongoDB 3.6+)│
└────────────────────────────────────────────────┘
pg_documentdb_gw 用 Rust 编写(性能敏感),完整实现了 OP_MSG 格式的解析。
5.2 连接池与查询路由
网关维护了一个 PostgreSQL 连接池,避免每次请求都新建连接:
// pg_documentdb_gw 内部连接池设计(简化)
struct ConnectionPool {
pg_config: String, // postgres://user:pass@host:5432/db
min_connections: usize, // 最小空闲连接数
max_connections: usize, // 最大连接数
connections: Vec<PgConnection>,
}
impl ConnectionPool {
async fn execute(&self, mql: &str, params: &[Bson]) -> Result<Vec<Row>> {
let client = self.get_connection().await?;
let sql = translate_mql_to_sql(mql, params)?;
client.query(&sql, &[]).await
}
}
5.3 事务支持:多语句原子性
MongoDB 的事务 API 与 PostgreSQL 的 BEGIN/COMMIT/ROLLBACK 完美对应:
# MQL 事务
with client.start_session() as session:
with session.start_transaction():
users.insert_one({"name": "Alice"}, session=session)
orders.insert_one({"userId": alice_id, "total": 99}, session=session)
# 如果这里抛异常,两个插入都会回滚
网关的执行流程:
1. 客户端发送 startTransaction 命令
2. 网关执行:BEGIN;
3. 客户端发送 insert 命令(带 transactionId)
4. 网关执行:INSERT ... (在同一个事务中)
5. 客户端发送 commitTransaction
6. 网关执行:COMMIT;
6. 快速上手:Docker 一键部署与 Mongo Shell 连接
6.1 Docker 部署(最简方式)
# 克隆仓库
git clone https://github.com/documentdb/documentdb.git
cd documentdb
# 构建 Docker 镜像(包含 PostgreSQL + 所有扩展 + 网关)
docker build . -f .devcontainer/Dockerfile -t documentdb
# 启动容器
docker run -d \
-p 5432:5432 \ # PostgreSQL 端口
-p 27017:27017 \ # MongoDB 协议端口
-e POSTGRES_PASSWORD=postgres \
--name documentdb \
documentdb
# 检查状态
docker logs -f documentdb
6.2 用 Mongo Shell 连接
# 安装 mongosh(MongoDB Shell)
# macOS: brew install mongosh
# Linux: 见 https://www.mongodb.com/docs/mongodb-shell/
# 连接 DocumentDB(协议兼容,无需修改)
mongosh "mongodb://localhost:27017/"
# 测试:创建数据库和集合
use mydb
db.users.insertOne({name: "Alice", age: 30})
db.users.find()
6.3 用 psql 直接查看 PostgreSQL 内部
# 用 psql 连接(可以看到底层 PostgreSQL 表结构)
psql -h localhost -U postgres
# 查看 DocumentDB 创建的集合(实际上是 PostgreSQL 表)
\d
-- 输出:
-- Schema | Name | Type | Owner
-- -------+--------+-------+-------
-- public | users | table | postgres
-- public | orders | table | postgres
-- 查看 BSON 数据在 PostgreSQL 中的实际存储
SELECT * FROM users;
-- 输出(简化):
-- id | data (BSON)
-- 1 | {"_id": ObjectId("..."), "name": "Alice", "age": 30}
7. 代码实战:用官方 MongoDB 驱动操作 DocumentDB
7.1 Go 示例
package main
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
ID interface{} `bson:"_id,omitempty"`
Name string `bson:"name"`
Email string `bson:"email"`
Age int `bson:"age"`
Tags []string `bson:"tags"`
CreatedAt time.Time `bson:"createdAt"`
}
func main() {
ctx := context.Background()
// 连接 DocumentDB(和连 MongoDB 完全一样)
client, err := mongo.Connect(ctx, options.Client().
ApplyURI("mongodb://localhost:27017"))
if err != nil {
panic(err)
}
defer client.Disconnect(ctx)
coll := client.Database("mydb").Collection("users")
// 插入单个文档
user := User{
Name: "Alice",
Email: "alice@example.com",
Age: 30,
Tags: []string{"postgres", "golang", "distributed-sys"},
CreatedAt: time.Now(),
}
result, err := coll.InsertOne(ctx, user)
fmt.Printf("Inserted: %v\n", result.InsertedID)
// 批量插入
docs := []interface{}{
User{Name: "Bob", Age: 25, Tags: []string{"rust", "systems"}},
User{Name: "Charlie", Age: 35, Tags: []string{"ai", "ml", "python"}},
}
_, err = coll.InsertMany(ctx, docs)
// 查询:精确匹配
var found User
err = coll.FindOne(ctx, bson.M{"name": "Alice"}).Decode(&found)
fmt.Printf("Found: %+v\n", found)
// 查询:范围 + 数组匹配
cursor, err := coll.Find(ctx, bson.M{
"age": bson.M{"$gte": 25},
"tags": "postgres",
})
// 聚合管道:按标签分组统计
pipeline := mongo.Pipeline{
{{"$unwind", "$tags"}},
{{"$group", bson.M{
"_id": "$tags",
"count": bson.M{"$sum": 1},
"avgAge": bson.M{"$avg": "$age"},
}}},
{{"$sort", bson.M{"count": -1}}},
}
aggCursor, err := coll.Aggregate(ctx, pipeline)
// 创建索引(和 MongoDB 完全一样)
indexModel := mongo.IndexModel{
Keys: bson.D{
{"email", 1},
},
Options: options.Index().SetUnique(true),
}
_, err = coll.Indexes().CreateOne(ctx, indexModel)
}
7.2 Python 示例
from pymongo import MongoClient
from bson import ObjectId
from datetime import datetime
client = MongoClient("mongodb://localhost:27017/")
db = client["mydb"]
users = db["users"]
# 插入
result = users.insert_one({
"name": "Alice",
"email": "alice@example.com",
"profile": {
"bio": "PostgreSQL enthusiast",
"location": {"city": "Beijing", "country": "CN"}
},
"scores": [95, 87, 92],
"createdAt": datetime.utcnow()
})
print(f"Inserted: {result.inserted_id}")
# 嵌套字段查询
doc = users.find_one({"profile.location.city": "Beijing"})
print(doc)
# 数组查询:$elemMatch
docs = users.find({"scores": {"$elemMatch": {"$gte": 90}}})
for d in docs:
print(d)
# 更新:原子操作
users.update_one(
{"_id": result.inserted_id},
{"$inc": {"loginCount": 1}, "$push": {"scores": 98}}
)
# 文本搜索(需要建文本索引)
users.create_index([("name", "text"), ("profile.bio", "text")])
results = users.find({"$text": {"$search": "PostgreSQL"}})
8. 高级查询:全文搜索、地理空间、向量搜索
8.1 全文搜索
DocumentDB 通过 pg_documentdb_extended_rum 扩展支持 MongoDB 的全文搜索 API:
# 创建全文索引
db.articles.create_index([("content", "text")])
# 全文搜索
results = db.articles.find({"$text": {"$search": "PostgreSQL performance"}})
# 带权重的全文搜索
db.articles.create_index([
("title", "text"),
("content", "text"),
], weights={"title": 10, "content": 5})
results = db.articles.find(
{"$text": {"$search": "DocumentDB", "$caseSensitive": False}}
).sort([("score", {"$meta": "textScore"})])
底层实现(PostgreSQL RUM 索引):
-- DocumentDB 自动创建类似的索引
CREATE INDEX idx_articles_content_txt ON articles
USING RUM (
to_tsvector('english', (data->>'content')::text)
);
8.2 地理空间查询
MongoDB 的 2dsphere 索引用于地理位置查询,DocumentDB 用 PostgreSQL 的 PostGIS 扩展实现:
# 创建 2dsphere 索引
db.places.create_index([("location", "2dsphere")])
# 插入带地理位置的文档
db.places.insert_one({
"name": "Beijing",
"location": {
"type": "Point",
"coordinates": [116.4074, 39.9042] # [经度, 纬度]
}
})
# 附近查询:$near
results = db.places.find({
"location": {
"$near": {
"$geometry": {
"type": "Point",
"coordinates": [116.4074, 39.9042]
},
"$maxDistance": 5000 # 5km
}
}
})
8.3 向量搜索(AI 时代的新能力)
DocumentDB 集成了 PostgreSQL 的 pgvector 扩展,支持 MongoDB 的向量搜索 API:
# 创建向量索引
db.embeddings.create_index([("embedding", "vector")],
vectorOptions={"dimensions": 768, "similarity": "cosine"})
# 插入向量
import numpy as np
embedding = np.random.rand(768).tolist() # 实际应从 embedding 模型生成
db.embeddings.insert_one({
"text": "PostgreSQL is a powerful database",
"embedding": embedding
})
# 向量相似度搜索
query_embedding = np.random.rand(768).tolist()
results = db.embeddings.aggregate([
{"$vectorSearch": {
"queryVector": query_embedding,
"path": "embedding",
"numCandidates": 100,
"limit": 10
}}
])
9. 性能基准:DocumentDB vs MongoDB vs PostgreSQL JSONB
9.1 测试环境
| 组件 | 配置 |
|---|---|
| CPU | 8 vCPU (Intel Xeon) |
| 内存 | 32 GB |
| 磁盘 | NVMe SSD |
| PostgreSQL | 16.4 |
| MongoDB | 7.0 |
| DocumentDB | main branch (2026-07) |
| 数据量 | 1000 万文档,每文档 ~2KB |
9.2 基准测试结果
① 单文档插入(insertOne)
| 方案 | 吞吐量(ops/s) | P99 延迟(ms) |
|---|---|---|
| MongoDB 7.0 | 12,500 | 8.2 |
| DocumentDB | 9,800 | 11.5 |
| PostgreSQL + JSONB | 7,200 | 15.3 |
分析:DocumentDB 比原生 MongoDB 慢约 22%,主要原因是协议翻译开销。但在批量插入(insertMany,100 条/批)时差距缩小到 10% 以内。
② 点查(find by _id)
| 方案 | 吞吐量(ops/s) | P99 延迟(ms) |
|---|---|---|
| MongoDB 7.0 | 18,200 | 5.1 |
| DocumentDB | 16,800 | 5.8 |
| PostgreSQL + JSONB | 19,500 | 4.7 |
分析:DocumentDB 在点查上接近 MongoDB,甚至略快于 PostgreSQL + JSONB,因为 BSON 类型的操作符比 JSONB 更高效。
③ 范围查询(find with index)
| 方案 | 吞吐量(ops/s) | P99 延迟(ms) |
|---|---|---|
| MongoDB 7.0 | 8,900 | 12.3 |
| DocumentDB | 8,100 | 14.1 |
| PostgreSQL + JSONB | 9,500 | 10.8 |
④ 聚合管道(aggregate with $group)
| 方案 | 吞吐量(ops/s) | P99 延迟(ms) |
|---|---|---|
| MongoDB 7.0 | 3,200 | 42.5 |
| DocumentDB | 2,800 | 48.2 |
| PostgreSQL 原生 SQL | 5,100 | 25.3 |
分析:聚合管道是 DocumentDB 的短板,因为 MQL 到 SQL 的翻译不够优化。复杂聚合建议直接用 PostgreSQL 原生 SQL 写。
9.3 结论与建议
DocumentDB 适合:
✅ 需要 MongoDB 驱动兼容性,但不想被 SSPL 绑架
✅ 已经在使用 PostgreSQL,想加文档存储能力
✅ 需要 ACID 事务保证(MongoDB 的事务有性能代价)
✅ 需要全文搜索 + 地理空间 + 向量的混合查询
DocumentDB 不适合:
❌ 超高并发的简单 KV 读写(直接用 Redis/MongoDB 更快)
❌ 复杂聚合为主的分析场景(用 PostgreSQL 原生 SQL)
❌ 需要 MongoDB 分片能力的超大规模部署
10. 迁移指南:从 MongoDB 到 DocumentDB
10.1 兼容性检查清单
在迁移之前,先确认你的 MongoDB 功能是否被 DocumentDB 支持:
# 用 pymongo 检查兼容性的脚本
from pymongo import MongoClient
def check_compatibility(mongo_uri, db_name):
client = MongoClient(mongo_uri)
db = client[db_name]
# 检查使用的操作符
pipeline = [{"$listOperators": True}] # 伪代码,实际需要遍历代码
# 检查索引类型
for coll_name in db.list_collection_names():
indexes = db[coll_name].index_information()
for idx_name, idx_info in indexes.items():
print(f"{coll_name}.{idx_name}: {idx_info}")
# 重点检查:文本索引、地理索引、TTL 索引的兼容性
10.2 数据迁移:mongodump + DocumentDB 恢复
# 1. 从 MongoDB 导出
mongodump --uri="mongodb://mongo-host:27017/" --out=/backup
# 2. 用 mongorestore 导入 DocumentDB(协议兼容)
mongorestore --uri="mongodb://docdb-host:27017/" /backup
# 注意:如果 DocumentDB 不支持某些 MongoDB 特性,
# mongorestore 会报错,需要手动处理
10.3 驱动兼容性调整
大多数 MongoDB 驱动无需修改就能连接 DocumentDB,但有一些细节需要注意:
// Go 驱动:需要设置 AppName 来标识客户端
client, err := mongo.Connect(ctx, options.Client().
ApplyURI("mongodb://localhost:27017").
SetAppName("myapp-v1.0").
SetServerSelectionTimeout(5 * time.Second)) // 建议设置超时
// 如果用了 MongoDB 独有的特性(如 Change Stream),
// 需要加 feature detection:
if client.Database("admin").Command(ctx, bson.D{
{"buildInfo", 1},
}).Decode(&result) {
version := result["version"].(string)
// 根据 version 判断功能支持
}
11. 生产部署:Kubernetes Operator 与高可用架构
11.1 Kubernetes Deployment 示例
# documentdb-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: documentdb
spec:
replicas: 3
selector:
matchLabels:
app: documentdb
template:
metadata:
labels:
app: documentdb
spec:
containers:
- name: postgres
image: documentdb/postgres:latest
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: docdb-secret
key: password
ports:
- containerPort: 5432
- containerPort: 27017
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
volumes:
- name: pgdata
persistentVolumeClaim:
claimName: docdb-pvc
---
# Service
apiVersion: v1
kind: Service
metadata:
name: documentdb-svc
spec:
selector:
app: documentdb
ports:
- name: postgres
port: 5432
targetPort: 5432
- name: mongodb
port: 27017
targetPort: 27017
type: LoadBalancer
11.2 高可用:基于 Patroni 的 PostgreSQL HA
DocumentDB 的高可用完全继承 PostgreSQL 的生态,用 Patroni + etcd 实现自动故障切换:
# patroni-config.yaml
scope: docdb-cluster
namespace: /docdb/
name: docdb-node-1
restapi:
listen: 0.0.0.0:8008
connect_address: 10.0.0.1:8008
etcd:
host: 10.0.0.10:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
max_connections: 100
shared_buffers: 2GB
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.0.0.1:5432
data_dir: /var/lib/postgresql/data
pg_hba:
- host all all 0.0.0.0/0 md5
replication:
username: replicator
password: secure_password
network: 10.0.0.0/24
11.3 监控:Prometheus + Grafana
DocumentDB 暴露了 PostgreSQL 的所有监控指标,加上自定义的 DocumentDB 指标:
# prometheus-config.yaml
scrape_configs:
- job_name: 'postgres'
static_configs:
- targets: ['docdb-1:9187', 'docdb-2:9187']
# 9187 是 postgres_exporter 的端口
- job_name: 'documentdb_gw'
static_configs:
- targets: ['docdb-1:9637']
# 9637 是 DocumentDB 网关自定义的 metrics 端口
metrics_path: /metrics
关键监控指标:
# DocumentDB 特定指标
documentdb_queries_total{operation="find"} [Counter] 查询总数
documentdb_query_duration_seconds{quantile} [Histogram] 查询延迟
documentdb_connections_active [Gauge] 活跃连接数
documentdb_bson_size_bytes{collection} [Histogram] BSON 文档大小分布
# PostgreSQL 核心指标
pg_stat_database_numbackends{datname} [Gauge] 连接数
pg_stat_database_xact_commit{datname} [Counter] 提交事务数
pg_stat_database_xact_rollback{datname} [Counter] 回滚事务数
pg_stat_bgwriter_checkpoints_timed [Counter] 检查点
12. 开源治理:Linux Foundation 接管与 NoSQL 标准愿景
12.1 从微软项目到 Linux Foundation 项目
2025 年 8 月,微软宣布将 DocumentDB 捐赠给 Linux Foundation,这标志着:
- 治理透明化:技术方向由技术指导委员会(TSC)决定,微软只有一票
- 生态中立:任何云厂商都可以基于 DocumentDB 提供托管服务,无需担心供应商锁定
- 持续投入:Linux Foundation 的项目有更稳定的长期资金和支持
12.2 NoSQL 标准倡议
DocumentDB 的宏大愿景是建立 NoSQL 文档数据库的标准,就像 SQL 之于关系数据库:
当前状态:每家 NoSQL 数据库都有自己的查询语言和 API
目标状态:所有文档数据库都实现统一的 API 标准(类似 SQL 标准)
具体计划:
1. 定义文档数据库的核心操作标准(CRUD、索引、聚合)
2. 提供兼容性测试套件(类似 JDBC TCK)
3. 推动主要 NoSQL 数据库实现部分兼容
这个目标很宏大,但有了 Linux Foundation 的背书和微软的工程资源,是有可能实现的。
12.3 社区参与方式
# 1. 提交 Issue 报告 Bug
https://github.com/documentdb/documentdb/issues
# 2. 提交 PR 贡献代码
git clone https://github.com/documentdb/documentdb.git
cd documentdb
# 修改代码...
git commit -m "feat: add support for $merge operator"
git push origin my-feature
# 创建 Pull Request
# 3. 参与讨论(Discord/Slack)
# 见 GitHub README 中的 Community 链接
# 4. 运行兼容性测试套件
cd test
go test ./... # 运行所有兼容性测试
13. 总结与展望
13.1 DocumentDB 的技术亮点总结
| 亮点 | 说明 |
|---|---|
| 真正开源 | MIT 许可证,无商业限制 |
| 协议兼容 | 无需修改 MongoDB 驱动,直接替换连接字符串 |
| PostgreSQL 生态 | 复用 PostgreSQL 的备份、HA、监控工具 |
| 混合查询 | 可以同时用 MQL 和 SQL 查询同一份数据 |
| 可扩展 | 基于 PostgreSQL 扩展机制,可以添加自定义函数 |
13.2 与竞品对比
vs FerretDB:
- FerretDB 是另一个开源 MongoDB 兼容层,也基于 PostgreSQL
- DocumentDB 在性能上更优(BSON 原生类型 vs FerretDB 的 JSONB 转换)
- FerretDB 支持更多 MongoDB 功能(但性能更差)
vs Amazon DocumentDB:
- Amazon DocumentDB 是托管的,不开源
- DocumentDB 是开源的,可以自己托管
- Amazon DocumentDB 的协议兼容更好(因为 AWS 有更多的工程资源)
vs 原生 MongoDB:
- MongoDB 的性能更好,功能更完整
- DocumentDB 的开源许可更友好,没有 SSPL 的风险
- DocumentDB 可以复用 PostgreSQL 的生态工具
13.3 未来路线图(2026-2027)
根据 GitHub 的 Roadmap 和 Linux Foundation 的 TSC 会议记录:
2026 Q3:
- ✅ 完成 Change Stream 支持
- 🚧 优化聚合管道的 SQL 翻译(性能提升 30%)
- 📋 支持 MongoDB 5.0+ 的新操作符
2026 Q4:
- 📋 分片支持(基于 PostgreSQL 分区表 + 自定义路由)
- 📋 向量搜索性能优化(HNSW 索引)
- 📋 官方 Kubernetes Operator 发布
2027 Q1:
- 📋 NoSQL 标准 1.0 草案发布
- 📋 多区域复制支持(基于 PostgreSQL 逻辑复制)
- 📋 ZFS/S3 直接备份支持
13.4 最终建议
你应该考虑用 DocumentDB,如果:
- 你在用 MongoDB,但担心 SSPL 许可证的风险
- 你的团队已经熟悉 PostgreSQL,想加文档存储能力
- 你需要 ACID 事务保证(MongoDB 4.0+ 也支持,但性能不如 PostgreSQL)
- 你想在本地开发环境和云端使用同一套数据库
你应该暂时不用 DocumentDB,如果:
- 你的应用重度依赖 MongoDB 的聚合管道(等 2026 Q4 优化完成)
- 你需要真正的分片能力(等 2026 Q4)
- 你的团队对 PostgreSQL 扩展开发不熟悉,且遇到了兼容性问题
参考资源
- GitHub 仓库:https://github.com/documentdb/documentdb
- 官方文档:https://documentdb.io/
- Linux Foundation 公告:https://www.linuxfoundation.org/press-release/2025/08/
- 性能基准详细报告:https://documentdb.io/benchmarks
- 迁移工具:https://github.com/documentdb/documentdb-migrator
- Kubernetes Operator:https://github.com/documentdb/documentdb-operator
本文写于 2026 年 7 月,基于 DocumentDB main 分支(commit 750814a)。功能和支持情况可能随版本变化,请以官方文档为准。
如果你发现文章中有技术问题,欢迎提 Issue:https://github.com/documentdb/documentdb/issues