

*"朝辞白帝彩云间,千里江陵一日还。"——李白*
老李最近接手了一个电商平台的重构项目。原来的 MySQL 表已经有 200 多个字段,每次产品加 SKU 属性都要改表结构,DBA 头大,研发也烦。商品详情页的 JSON 塞满了 text 字段,全文检索慢到 10 秒以上。更糟糕的是,推荐系统刚上了向量召回,数据团队说要再搭一套 Milvus……
老李掰着手指算了算:MySQL + Redis + Elasticsearch + Milvus,四套系统,四套运维,四份钱。
决策时刻:MongoDB 8.x 能不能一套搞定?
答案是:大部分场景,能。
*"问渠那得清如许,为有源头活水来。"——朱熹*
MongoDB 是一个**文档型 NoSQL 数据库**,数据以 BSON(Binary JSON)格式存储,每条记录是一个"文档",文档聚合为"集合",集合属于"数据库"。
**当前最新稳定版本**:MongoDB 8.0(LTS,长期支持版本),补丁版本 8.0.17(截至 2025 年 4 月)。8.2 系列已发布但属于 Rapid Release,生产环境推荐 **8.0.x LTS**。
核心能力矩阵:
| 能力 | MongoDB 8.0 支持 | 说明 |
|------|-----------------|------|
| 文档存储 | ✅ | BSON,支持嵌套、数组 |
| 聚合管道 | ✅ | 强大的 ETL、统计 |
| ACID 事务 | ✅ | 4.0+ 支持多文档事务 |
| 全文检索 | ✅ | Atlas Search(本地需 Atlas) |
| 向量搜索 | ✅ | Atlas Vector Search,支持 4096 维 |
| 变更流 | ✅ | CDC,实时事件驱动 |
| 时序集合 | ✅ | 5.0+ 原生时序,自动分桶 |
| 地理空间查询 | ✅ | 2dsphere,GeoJSON |
| 分片集群 | ✅ | 水平扩展 |
*"工欲善其事,必先利其器。"——孔子*

最适合 MongoDB 的场景:
不适合的场景:

*"纸上得来终觉浅,绝知此事要躬行。"——陆游*
version: '3.8'
services:
mongodb:
image: mongo:8.0
container\_name: mongodb
restart: unless-stopped
ports:
- "27017:27017"
environment:
MONGO\_INITDB\_ROOT\_USERNAME: root
MONGO\_INITDB\_ROOT\_PASSWORD: example123
MONGO\_INITDB\_DATABASE: demo
volumes:
- mongo\_data:/data/db
- ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
networks:
- mongo-net
mongo-express:
image: mongo-express:1.0.2
container\_name: mongo-express
restart: unless-stopped
ports:
- "8081:8081"
environment:
ME\_CONFIG\_MONGODB\_ADMINUSERNAME: root
ME\_CONFIG\_MONGODB\_ADMINPASSWORD: example123
ME\_CONFIG\_MONGODB\_URL: mongodb://root:example123@mongodb:27017/
ME\_CONFIG\_BASICAUTH\_USERNAME: admin
ME\_CONFIG\_BASICAUTH\_PASSWORD: admin123
depends\_on:
- mongodb
networks:
- mongo-net
volumes:
mongo\_data:
networks:
mongo-net:
driver: bridgedb = db.getSiblingDB('demo');
db.createUser({
user: 'demouser',
pwd: 'demo123',
roles: [{ role: 'readWrite', db: 'demo' }]
});
db.products.insertMany([
{ name: 'iPhone 16', price: 7999, tags: ['手机', '苹果'], stock: 100 },
{ name: 'MacBook Pro', price: 14999, tags: ['电脑', '苹果'], stock: 50 }
]);docker-compose up -d
# 验证
docker ps
curl http://localhost:8081 # 打开监控界面,用户名: admin / admin123**📸 监控截图说明**:访问
http://localhost:8081后,Mongo-Express 界面会显示数据库列表、集合、文档内容,支持 CRUD 操作,界面如下所示(浏览器灰底绿字风格):左侧:数据库/集合树形导航 中部:文档列表(JSON 展示) 右上:查询过滤框 操作按钮:Add Document / Edit / Delete
mongo-demo/
├── pom.xml
├── src/main/java/com/example/
│ ├── MongoApplication.java
│ ├── model/
│ │ ├── Product.java # 商品(基础CRUD)
│ │ ├── Order.java # 订单(事务)
│ │ └── SensorData.java # 传感器(时序)
│ ├── repository/
│ │ ├── ProductRepository.java
│ │ └── OrderRepository.java
│ ├── service/
│ │ ├── ProductService.java
│ │ └── VectorSearchService.java
│ └── controller/
│ ├── ProductController.java
│ └── OrderController.java
└── src/main/resources/
└── application.yml<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 用于事务支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>spring:
data:
mongodb:
uri: mongodb://demouser:demo123@localhost:27017/demo
database: demo
server:
port: 8080**场景设计**:电商平台商品管理,不同类目属性不同(手机有内存、电脑有 CPU),无需改表结构。
// Product.java
@Document(collection = "products")
@Data
@Builder
public class Product {
@Id
private String id;
private String name;
private Double price;
private List<String> tags;
private Integer stock;
// 灵活的扩展属性,不同商品有不同字段
private Map<String, Object> attributes;
@CreatedDate
private Date createdAt;
}
// ProductRepository.java
public interface ProductRepository extends MongoRepository<Product, String> {
List<Product> findByTagsContaining(String tag);
List<Product> findByPriceBetween(Double min, Double max);
@Query("{ 'attributes.color': ?0 }")
List<Product> findByColor(String color);
}
// ProductController.java
@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductRepository productRepo;
@PostMapping
public Product create(@RequestBody Product p) {
return productRepo.save(p);
}
@GetMapping
public List<Product> list() {
return productRepo.findAll();
}
@GetMapping("/tag/{tag}")
public List<Product> byTag(@PathVariable String tag) {
return productRepo.findByTagsContaining(tag);
}
@GetMapping("/price")
public List<Product> byPrice(@RequestParam double min, @RequestParam double max) {
return productRepo.findByPriceBetween(min, max);
}
}**页面设计**(简单 Thymeleaf 或前端调用):
**验证命令**:
# 创建带灵活属性的商品
curl -X POST http://localhost:8080/api/products \
-H "Content-Type: application/json" \
-d '{"name":"华为 Mate 70","price":5999,"tags":["手机","华为"],
"stock":200,"attributes":{"内存":"16GB","存储":"512GB","颜色":"黑色"}}'
# 按标签查询
curl http://localhost:8080/api/products/tag/手机
# 预期输出:返回所有手机类商品 JSON 数组**场景设计**:订单统计仪表盘——按品类统计销售额、平均客单价。
// ProductService.java
@Service
@RequiredArgsConstructor
public class ProductService {
private final MongoTemplate mongoTemplate;
public List<Document> salesStatsByCategory() {
Aggregation agg = Aggregation.newAggregation(
Aggregation.unwind("tags"),
Aggregation.group("tags")
.count().as("count")
.sum("price").as("totalRevenue")
.avg("price").as("avgPrice"),
Aggregation.sort(Sort.by(Sort.Direction.DESC, "totalRevenue")),
Aggregation.limit(10)
);
AggregationResults<Document> result =
mongoTemplate.aggregate(agg, "products", Document.class);
return result.getMappedResults();
}
}
// 对应 Controller 端点
@GetMapping("/stats/category")
public List<Document> categoryStats() {
return productService.salesStatsByCategory();
}**验证**:
curl http://localhost:8080/api/products/stats/category
# 预期返回:[{"\_id":"手机","count":5,"totalRevenue":35000,"avgPrice":7000}, ...]**场景设计**:下单扣库存——订单创建和库存扣减必须原子执行。
⚠️ 事务需要 **Replica Set** 模式,单节点需启用:
mongod --replSet rs0,或 Docker 镜像启动参数添加--replSet rs0
// Order.java
@Document(collection = "orders")
@Data
@Builder
public class Order {
@Id
private String id;
private String productId;
private Integer quantity;
private Double totalAmount;
private String status; // PENDING, CONFIRMED, CANCELLED
@CreatedDate
private Date createdAt;
}
// OrderService.java
@Service
@RequiredArgsConstructor
public class OrderService {
private final MongoTemplate mongoTemplate;
private final ProductRepository productRepo;
private final OrderRepository orderRepo;
@Transactional // Spring 事务,需配置 MongoTransactionManager
public Order createOrder(String productId, Integer qty) {
Product product = productRepo.findById(productId)
.orElseThrow(() -> new RuntimeException("商品不存在"));
if (product.getStock() < qty) {
throw new RuntimeException("库存不足");
}
// 扣库存
product.setStock(product.getStock() - qty);
productRepo.save(product);
// 创建订单
Order order = Order.builder()
.productId(productId)
.quantity(qty)
.totalAmount(product.getPrice() \* qty)
.status("CONFIRMED")
.build();
return orderRepo.save(order);
}
}
// 配置事务管理器
@Configuration
public class MongoConfig {
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
}**验证**:
# 正常下单
curl -X POST "http://localhost:8080/api/orders?productId=XXX&qty=2"
# 超卖测试(库存不足时事务回滚)
curl -X POST "http://localhost:8080/api/orders?productId=XXX&qty=9999"
# 预期:返回 "库存不足" 错误,数据库中订单和库存均不变**场景设计**:库存预警——当商品库存低于阈值时,实时推送告警。
// ChangeStreamListener.java
@Component
@RequiredArgsConstructor
@Slf4j
public class InventoryChangeListener implements ApplicationRunner {
private final MongoTemplate mongoTemplate;
@Override
public void run(ApplicationArguments args) {
// 异步监听,生产环境用线程池
new Thread(() -> {
ChangeStreamOptions options = ChangeStreamOptions.builder()
.filter(Aggregation.newAggregation(
Aggregation.match(
Criteria.where("operationType").in("update", "replace")
)
)).build();
mongoTemplate.changeStream("products",
options, Document.class)
.doOnNext(event -> {
Document body = event.getBody();
if (body != null) {
Integer stock = body.getInteger("stock");
if (stock != null && stock < 10) {
log.warn("⚠️ 库存预警!商品 {} 库存仅剩 {}",
body.getString("name"), stock);
// 实际生产:推送钉钉/企微告警
}
}
})
.subscribe();
}).start();
}
}**场景设计**:IoT 温度传感器数据存储,自动按时间分桶压缩。
// 通过 MongoTemplate 创建时序集合
@Configuration
public class TimeSeriesConfig {
@Bean
public CommandLineRunner createTimeSeriesCollection(MongoTemplate mongoTemplate) {
return args -> {
if (!mongoTemplate.collectionExists("sensor\_data")) {
mongoTemplate.createCollection("sensor\_data",
CollectionOptions.empty()
.timeSeries(
TimeSeriesOptions.timeSeries("timestamp")
.metaField("sensorId")
.granularity(TimeSeriesGranularity.MINUTES)
));
}
};
}
}
// SensorData.java
@Document(collection = "sensor\_data")
@Data
@Builder
public class SensorData {
private String sensorId; // metaField
private Date timestamp; // timeField
private Double temperature;
private Double humidity;
}
// 批量插入传感器数据
@PostMapping("/sensor/batch")
public void insertSensorData() {
List<SensorData> batch = IntStream.range(0, 100)
.mapToObj(i -> SensorData.builder()
.sensorId("sensor-001")
.timestamp(new Date(System.currentTimeMillis() - i \* 60000L))
.temperature(20.0 + Math.random() \* 10)
.humidity(60.0 + Math.random() \* 20)
.build())
.collect(Collectors.toList());
mongoTemplate.insertAll(batch);
}**场景设计**:周边餐厅搜索,查找用户 5km 范围内的门店。
// Store.java
@Document(collection = "stores")
@Data
public class Store {
@Id
private String id;
private String name;
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO\_2DSPHERE)
private GeoJsonPoint location; // [longitude, latitude]
}
// 查询 5km 范围内的门店
public List<Store> findNearby(double lng, double lat, double radiusKm) {
Point point = new Point(lng, lat);
Distance distance = new Distance(radiusKm, Metrics.KILOMETERS);
return mongoTemplate.find(
new Query(Criteria.where("location")
.nearSphere(point).maxDistance(distance.getNormalizedValue())),
Store.class
);
}*"凡事预则立,不预则废。"——《礼记》*

version: '3.8'
services:
mongo1:
image: mongo:8.0
container\_name: mongo1
command: mongod --replSet rs0 --bind\_ip\_all
ports:
- "27017:27017"
volumes:
- mongo1\_data:/data/db
networks:
- rs-net
mongo2:
image: mongo:8.0
container\_name: mongo2
command: mongod --replSet rs0 --bind\_ip\_all
ports:
- "27018:27017"
volumes:
- mongo2\_data:/data/db
networks:
- rs-net
mongo3:
image: mongo:8.0
container\_name: mongo3
command: mongod --replSet rs0 --bind\_ip\_all
ports:
- "27019:27017"
volumes:
- mongo3\_data:/data/db
networks:
- rs-net
volumes:
mongo1\_data:
mongo2\_data:
mongo3\_data:
networks:
rs-net:
driver: bridge# 启动容器
docker-compose up -d
# 进入 mongo1 初始化副本集
docker exec -it mongo1 mongosh
# 在 mongosh 中执行
rs.initiate({
\_id: "rs0",
members: [
{ \_id: 0, host: "mongo1:27017", priority: 2 },
{ \_id: 1, host: "mongo2:27017", priority: 1 },
{ \_id: 2, host: "mongo3:27017", priority: 1 }
]
})
# 验证状态
rs.status()spring:
data:
mongodb:
uri: mongodb://root:example123@localhost:27017,localhost:27018,localhost:27019/demo?replicaSet=rs0&authSource=admin# 停掉主节点,观察自动切换
docker stop mongo1
# 等待约 10 秒,进入 mongo2 查看
docker exec -it mongo2 mongosh --eval "rs.status()"
# 预期:mongo2 或 mongo3 变成 PRIMARY
| 问题 | 现象 | 解决方案 |
|------|------|----------|
| 连接超时 | Connection refused | 检查 Docker 网络、端口映射、authSource |
| 事务不生效 | 无报错但未回滚 | 必须配置 Replica Set,单机模式不支持事务 |
| 慢查询 | 接口响应 >1s | 执行 db.collection.createIndex({"field":1}),避免全集合扫描 |
| 内存溢出 | OOM Killer | 配置 --wiredTigerCacheSizeGB 1(容器限制 2G 时建议 0.5G) |
| 主从延迟 | 读到旧数据 | 设置 readPreference=primary 或使用 writeConcern majority |
| 文档超 16MB | 写入报错 | 使用 GridFS 存储大文件 |
| 索引过多 | 写入变慢 | 删除冗余索引,单集合索引数量建议 ≤ 10 |
*"名不正则言不顺,言不顺则事不成。"——孔子*
MongoDB 自 2018 年 10 月起将 Community Server 改为 **SSPL v1(Server Side Public License)**。SSPL 基于 GPL v3,但修改了第 13 条:
**核心条款**:如果你将 MongoDB(或基于它的软件)**作为服务对外提供**,必须将你运行该服务所用的**全部源代码**(包括监控、备份、API 网关、管理界面等)全部以 SSPL 开源。
⚠️ SSPL **未获 OSI 认可**,Red Hat、Debian、Fedora 均因此移除了 MongoDB 包。
| 用途 | 是否受限 | 说明 |
|------|---------|------|
| 内部系统使用 MongoDB | ✅ 不受限 | 给自己公司内部用,不触发 SSPL |
| 给子公司提供 MongoDB 服务 | ✅ 不受限 | 关联公司视为内部 |
| SaaS 产品中嵌入 MongoDB | ⚠️ 需谨慎 | 若"以服务形式提供 MongoDB 功能"则触发 |
| 作为云数据库服务对外售卖 | ❌ 必须开源全栈或商业授权 | 触发 SSPL 第 13 条 |
| 将 MongoDB 封装为 DBaaS | ❌ 必须商业授权 | 如 AWS DocumentDB 的做法 |
**结论**:绝大多数企业**内部使用 MongoDB**完全合规,无需付费。只有**对外提供数据库即服务**才会触发 SSPL 约束。
需要商业许可的场景:选择 **MongoDB Enterprise Advanced**,获得正式 SLA、加密静态数据、审计日志、Ops Manager 等企业功能。
*"海纳百川,有容乃大。"——林则徐*

注意:Atlas Vector Search 需要 MongoDB Atlas(云服务),本地 MongoDB 8.0 使用
$vectorSearch需在 Replica Set 或 Atlas 上开启向量索引。社区版可使用余弦相似度自行计算,或升级到 Atlas Local Dev。
// 存储文档向量(使用本地 embedding 模型或调用 API)
@Document(collection = "knowledge\_base")
@Data
public class KnowledgeDoc {
@Id
private String id;
private String content; // 原始文本
private String category; // 分类标签
private List<Double> embedding; // 向量,1536 维(OpenAI text-embedding-3-small)
private Date createdAt;
}
// VectorSearchService.java
@Service
@RequiredArgsConstructor
public class VectorSearchService {
private final MongoTemplate mongoTemplate;
// Atlas 环境下的向量搜索
public List<KnowledgeDoc> semanticSearch(List<Double> queryVector, int topK) {
// $vectorSearch 聚合管道(需 Atlas 或开启向量索引)
String pipeline = String.format(
"[{ \"$vectorSearch\": { \"index\": \"vector\_index\", " +
"\"path\": \"embedding\", \"queryVector\": %s, " +
"\"numCandidates\": %d, \"limit\": %d } }]",
queryVector.toString(), topK \* 10, topK);
return mongoTemplate.aggregate(
Aggregation.newAggregation(
Aggregation.stage(Document.parse(
"{ $vectorSearch: { index: 'vector\_index', " +
"path: 'embedding', queryVector: " + queryVector +
", numCandidates: 200, limit: " + topK + " } }"))
),
"knowledge\_base",
KnowledgeDoc.class
).getMappedResults();
}
}在使用 MongoDB 进行 AI Coding 时,老李强烈推荐以下工具组合:
| 工具 | 推荐理由 |
|------|----------|
| **Claude Code** | 直接在终端操作,可生成 Spring Boot + MongoDB 完整项目结构,支持文件读写、测试运行 |
| **Claude in VS Code** | 代码补全时理解 MongoRepository 语义,能自动生成聚合管道 |
| **Claude for Excel** | 将 MongoDB 导出数据进行分析,快速生成 BI 报表 |
**理由**:
Aggregation.newAggregation() 代码# 典型 AI Coding 工作流
claude "帮我用 Spring Boot 2 写一个基于 MongoDB 向量搜索的客服问答系统,
包含知识库录入接口、语义搜索接口,使用 text-embedding-3-small 向量化"
# → Claude Code 直接生成代码文件、pom.xml、application.yml,并运行测试*"择其善者而从之,其不善者而改之。"——孔子*
| 对比维度 | MongoDB 8.0 | FerretDB 2.x | DocumentDB(AWS) | CouchDB 3.x |
|---------|------------|--------------|------------------|-------------|
| 开源协议 | SSPL(非 OSI) | Apache 2.0 ✅ | 专有 | Apache 2.0 ✅ |
| MongoDB 兼容 | 原生 | 大部分兼容 | 大部分兼容 | ❌ 不兼容 |
| 向量搜索 | ✅ Atlas | ✅ 2.0 支持 | ❌ | ❌ |
| ACID 事务 | ✅ | ✅(依赖 PG) | ✅ | ❌ 最终一致 |
| 存储引擎 | WiredTiger | PostgreSQL | 自研 | 自研 |
| 性能 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 运维复杂度 | 中 | 低(依赖 PG) | 低(托管) | 低 |
| 适用场景 | 全场景 | 替代 MongoDB 开源 | AWS 生态 | 移动端离线同步 |
FerretDB 2.0 基于 PostgreSQL + DocumentDB 扩展,兼容 MongoDB 5.0+ 协议,性能提升最高 20 倍,使用 Apache 2.0 许可。
# 快速体验(一行命令,包含 PostgreSQL + FerretDB + mongosh)
docker run -d --rm --name ferretdb -p 27017:27017 \
-e POSTGRES\_USER=ferret \
-e POSTGRES\_PASSWORD=ferret123 \
ghcr.io/ferretdb/ferretdb-eval:2
# 连接测试(兼容 MongoDB 驱动)
mongosh mongodb://ferret:ferret123@localhost:27017/
# Spring Boot 连接:直接替换 URI 即可,驱动无需修改
spring:
data:
mongodb:
uri: mongodb://ferret:ferret123@localhost:27017/demo**迁移注意事项**:
$text 全文搜索(用 PostgreSQL 全文替代)docker run -d --name couchdb \
-e COUCHDB\_USER=admin \
-e COUCHDB\_PASSWORD=admin123 \
-p 5984:5984 \
couchdb:3.3
# 访问 Fauxton 管理界面
open http://localhost:5984/\_utilsCouchDB 主打**最终一致性**和**移动端离线同步**(PouchDB),不兼容 MongoDB 协议,迁移成本较高。

**小结**:对于中小团队,将 MongoDB 作为"多能数据库"可以大幅降低系统复杂度。但超大规模生产场景仍需按需拆分专业组件。

**小结**:99% 的企业内部应用场景完全合规。若担心许可证风险,FerretDB 2.0 是最接近 1:1 替代的选择。

**小结**:相比单独部署 Milvus 等向量数据库,MongoDB 的最大优势是**向量与业务数据同库**,省去数据同步管道,查询时可同时过滤业务条件(品类、价格、库存)再做向量召回,精度更高。
*"路漫漫其修远兮,吾将上下而求索。"——屈原*

| 阶段 | 行动 | 工具/命令 |
|------|------|-----------|
| 快速验证 | Docker 单机启动 | docker-compose up -d |
| 开发调试 | Mongo-Express 监控 | http://localhost:8081 |
| 代码生成 | Claude Code 辅助 | claude "生成 MongoDB 聚合统计代码" |
| 上生产 | 三节点 Replica Set | rs.initiate({...}) |
| 许可证担忧 | 替换为 FerretDB | docker run ferretdb-eval:2 |
| AI 应用 | 向量搜索 + RAG | Spring AI + MongoDBAtlasVectorStore |
**立即行动**:
git clone 本文示例代码docker-compose up -d 启动单机环境http://localhost:8081 验证监控界面*基于 MongoDB 8.0 官方文档和 2025 年社区最佳实践撰写。*
*参考资料:MongoDB 官方文档 docs.mongodb.com · FerretDB 官网 ferretdb.com · Spring AI 文档 spring.io*
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。