Java高级全套教程(六)—— MongoDB超详细实战详解

一、非关系型数据库概述与MongoDB核心定位

1.1 传统关系型数据库的生产痛点

在互联网高速迭代的业务场景中,MySQL、Oracle等传统关系型数据库(RDBMS)凭借事务一致性、结构化存储的优势,长期占据企业数据存储主流场景。但随着海量用户、高并发读写、动态数据模型、海量非结构化数据场景的普及,关系型数据库的架构短板逐渐凸显,无法适配多元化的互联网业务需求。

其核心痛点主要集中在四大维度:

  • 数据模型固化:关系型数据库依赖固定表结构,字段、类型、关联关系创建后难以灵活修改,面对需求频繁迭代的互联网项目,数据表变更成本极高,极易引发业务兼容问题。

  • 高并发读写瓶颈:传统数据库依赖磁盘IO、事务锁机制,在万级以上QPS的高并发读写场景中,锁竞争、磁盘读写延迟会严重拖慢系统响应速度,性能优化难度大。

  • 非结构化数据适配差:对于用户画像、设备日志、社交动态、地理位置信息等非结构化、半结构化数据,无法高效存储,通常需要拆表、冗余存储,导致表结构臃肿、查询效率低下。

  • 横向扩容成本高:关系型数据库的主从复制、分库分表架构复杂,扩容需要迁移数据、修改配置、适配路由,无法实现无感弹性扩容,难以支撑业务爆发式增长。

1.2 NoSQL数据库分类与适用场景

NoSQL(Not Only SQL)即非关系型数据库,是为解决传统RDBMS短板而生的分布式数据存储方案,摒弃了固定表结构、事务强一致性、主外键关联特性,以高并发、高可用、高伸缩、灵活数据模型为核心优势,主打最终一致性,适配互联网海量数据场景。主流NoSQL数据库分为四大类:

  • 键值型(Key-Value):代表产品Redis、Memcached,数据以键值对存储,读写速度极快,主要用于缓存、秒杀、会话存储场景。

  • 文档型(Document):代表产品MongoDB、CouchDB,数据以类JSON文档存储,结构灵活,适配动态数据模型、海量非结构化数据场景。

  • 列存储型(Column):代表产品HBase、Cassandra,以列为单位存储数据,适合海量日志、大数据离线分析场景。

  • 图数据库(Graph):代表产品Neo4j,专注于节点与关系存储,适配社交关系、知识图谱、推荐算法场景。

1.3 MongoDB简介与核心优势

MongoDB是一款开源、跨平台、高性能、无模式的文档型NoSQL数据库,由10gen公司开发,命名源自humongous(海量的、巨大的),主打海量数据存储与高并发读写场景。MongoDB摒弃了复杂的事务和固定结构,以BSON二进制文档为核心存储单元,完美适配互联网动态业务场景,是中小企业非结构化数据存储的首选方案。

相较于传统数据库及其他NoSQL数据库,MongoDB核心优势极为突出:

  • 无模式灵活存储:集合无需固定结构,不同文档可拥有不同字段、不同数据类型,支持动态增删改字段,适配需求快速迭代的项目。

  • 类JSON嵌套结构:支持文档嵌套、数组存储,可将关系型数据库多表关联数据整合为单文档,彻底规避多表JOIN查询的性能损耗。

  • 超高读写性能:基于内存映射文件机制,大部分热点数据读写走内存,支持万级QPS,远超传统关系型数据库。

  • 无感弹性扩容:支持分片集群横向扩容,无需停机、无需迁移数据,可按需扩容节点,适配海量数据增长。

  • 高可用容灾机制:支持副本集多节点备份,自动故障转移,单节点宕机不影响整体服务,保障数据安全与服务稳定性。

  • 丰富的索引与查询能力:支持单键、复合、地理空间、全文、哈希等多种索引,查询语法接近SQL,学习成本低。

  • 完善的生态适配:完美适配Java、Python、Go等主流开发语言,支持SpringBoot快速集成,运维工具丰富,落地成本低。

1.4 MongoDB企业级适用场景与选型标准

1.4.1 核心适用场景

  • 互联网网站业务:适配用户信息、用户画像、文章评论、朋友圈动态等实时读写场景,支持高并发插入、更新、查询,具备完善的集群伸缩能力。

  • 海量日志存储:存储物联网设备日志、系统操作日志、用户行为日志,支持海量低价值数据的高效写入与多维度分析。

  • 缓存中间层:可作为持久化缓存层,弥补Redis内存缓存断电丢失的短板,系统重启后可快速恢复缓存数据,避免底层数据源压力过载。

  • 地理位置服务:依托地理空间索引,实现附近的人、附近门店、轨迹定位等功能,广泛应用于外卖、打车、社交、物流场景。

  • 动态表单业务:电商商品参数、自定义表单、企业审批流程等动态字段业务,无需频繁修改数据表,适配多变的业务模型。

  • 游戏业务:存储游戏用户角色、装备、积分、战绩等信息,通过嵌套文档一站式存储关联数据,查询更新效率极高。

1.4.2 选型判定标准

满足以下任意2项及以上条件,优先选择MongoDB,可最大化发挥其性能优势:

  • 业务无需复杂事务、跨表JOIN关联,仅需单文档数据一致性;

  • 项目处于快速迭代阶段,数据模型不固定,需要频繁调整字段结构;

  • 业务读写QPS高达2000+,需要支撑高并发实时读写;

  • 数据量级达TB/PB级别,需要低成本海量数据存储方案;

  • 业务增长迅速,需要数据库支持快速横向扩容;

  • 需要大量地理位置查询、全文模糊检索、数组维度查询场景;

  • 业务要求99.999%高可用,需要自动故障转移、数据多副本容灾。

二、MongoDB核心数据模型与底层原理

2.1 MongoDB与关系型数据库映射关系

MongoDB的逻辑架构与MySQL高度相似,核心概念可一一对应,降低开发者学习门槛,二者核心映射关系如下:

关系型数据库(RDBMS) MongoDB 核心说明
数据库(Database) 数据库(Database) 独立的数据存储单元,相互隔离
数据表(Table) 集合(Collection) 存储一组同类文档,无固定结构约束
数据行(Row) 文档(Document) 最小数据存储单元,BSON格式
数据列(Column) 字段(Field) 文档中的单个键值对,无固定类型约束
主键(Primary Key) _id字段 文档唯一标识,自动生成全局唯一值
索引(Index) 索引(Index) 支持多类型索引,优化查询性能
JOIN关联查询 嵌套文档/引用关联 通过嵌套替代多表关联,提升查询效率

2.2 BSON数据格式详解

MongoDB所有数据均以**BSON(Binary JSON)**二进制格式存储,是JSON的超集,保留了JSON轻量、易解析的特性,同时拓展了更多专业数据类型,适配数据库持久化存储场景。BSON核心特性为:轻量性、可遍历性、高效性,读写效率远高于普通JSON格式。

MongoDB文档支持的全量数据类型及实操示例如下,覆盖企业开发所有常用场景:

数据类型 类型说明 实操示例
String 字符串类型,最常用数据类型 {username: “zhangsan”, gender: “male”}
Int32/Int64 32位/64位整型数值 {age: 25, score: 98}
Double 双精度浮点型,存储小数 {height: 175.5, weight: 65.2}
Boolean 布尔类型,true/false {isVip: true, isDelete: false}
ObjectId 文档唯一ID,系统自动生成 {_id: ObjectId(“62a00e5c1eb2c6ab85dd5eec”)}
Array 数组类型,存储多个同/异类数据 {hobbies: [“跑步”, “阅读”, “编程”], tags: [1,2,3]}
Embedded Document 内嵌文档,实现数据嵌套 {address: {province: “广东”, city: “深圳”}}
Date 标准日期时间类型 {createTime: new Date(), birthday: ISODate(“1999-01-01”)}
Null 空值类型 {remark: null}
Binary 二进制数据,存储图片、文件流 {fileData: BinData(0, “xxxxxx”)}
Timestamp 时间戳,用于数据版本控制 {updateTs: new Timestamp()}

2.3 数据模型设计:内嵌与引用选型

MongoDB无主外键关联机制,通过内嵌文档引用关联两种方式实现数据关联,合理选择数据模型是提升查询性能的核心,需根据业务关系灵活选型。

2.3.1 内嵌文档模型

将关联数据直接嵌套在主文档内部,数据集中存储,一次查询即可获取所有关联数据,无多表查询开销,是MongoDB推荐的优先选型方案。

适用场景:一对一、一对多关联关系;数据需要频繁联合查询;子数据不会独立频繁修改;数据嵌套层级可控。

实战案例(用户内嵌地址、爱好信息)

{
  "_id": ObjectId("65001234567890abcdef1234"),
  "username": "lisi",
  "age": 28,
  "isVip": true,
  "hobbies": ["游泳", "骑行", "摄影"],
  "address": {
    "province": "浙江省",
    "city": "杭州市",
    "district": "西湖区"
  },
  "createTime": ISODate("2025-01-01T10:00:00Z")
}

2.3.2 引用关联模型

将关联数据单独存储为独立文档,通过存储对方文档的_id实现关联,类似关系型数据库的外键,避免数据冗余,适合复杂关联场景。

适用场景:多对多复杂关联;子数据频繁独立修改;内嵌会导致数据严重冗余;嵌套层级过深的大数据集。

实战案例(用户与订单引用关联)

// 用户主文档
{
  "_id": ObjectId("65001234567890abcdef1234"),
  "username": "lisi",
  "age": 28,
  "orderIds": [
    ObjectId("65001234567890abcdef1235"),
    ObjectId("65001234567890abcdef1236")
  ]
}

// 独立订单文档
{
  "_id": ObjectId("65001234567890abcdef1235"),
  "orderNo": "ORD20250526001",
  "amount": 299.9,
  "status": "已完成",
  "userId": ObjectId("65001234567890abcdef1234")
}

三、MongoDB存储引擎深度解析

存储引擎是MongoDB的核心底层组件,负责管理数据在内存和磁盘的存储、读写、缓存、持久化逻辑,直接决定数据库的性能、并发能力、数据可靠性。MongoDB支持多款存储引擎,不同引擎适配不同业务场景。

3.1 WiredTiger存储引擎(默认)

WiredTiger是MongoDB 3.2及以上版本的默认存储引擎,完全替代了老旧的MMAPv1引擎,主打高并发、高压缩、低资源占用,适配99%的企业生产场景。

3.1.1 核心核心特性

  • 文档级并发控制:支持单文档粒度的读写锁,多客户端可同时修改同一集合的不同文档,彻底解决旧引擎集合级锁的并发瓶颈,高并发性能大幅提升。

  • MVCC多版本并发控制:读写操作互不阻塞,读操作读取数据快照,写操作独立执行,无读写锁竞争,保障高并发场景下的响应速度。

  • 自动数据压缩:默认对集合数据开启块压缩、对索引开启前缀压缩,以少量CPU开销换取极高的磁盘利用率,可节省30%-80%磁盘空间。

  • 快照与检查点机制:每60秒自动生成数据检查点,将内存快照持久化到磁盘,保证数据一致性,宕机后可通过日志快速恢复数据。

  • 完善的日志持久化:通过WAL预写日志记录所有数据修改操作,检查点之间的增量修改可通过日志回放恢复,彻底避免数据丢失。

3.1.2 内存缓存机制

WiredTiger的内存缓存大小自动适配服务器配置,默认取值规则为:取(物理内存-1GB)的50% 和 256MB 中的较大值,最大化利用内存提升热点数据读写速度。

示例:服务器内存1.25GB时,缓存大小为256MB;服务器内存16GB时,缓存大小为7.5GB。

3.2 In-Memory内存存储引擎

In-Memory是MongoDB企业版专属的内存存储引擎,所有业务数据仅存储在内存中,不持久化到磁盘,读写速度达到极致,仅适配超高并发、对数据持久化要求低的临时业务场景。

3.2.1 核心特性

  • 纯内存读写:无磁盘IO开销,读写性能远超WiredTiger,支撑十万级超高并发。

  • 文档级并发锁:同样支持多客户端并发修改不同文档,并发能力优异。

  • 无持久化能力:服务器重启、宕机后,所有业务数据全部丢失,仅保留少量元数据、索引临时文件。

3.2.2 自定义内存配置

默认占用50%物理内存,可通过配置文件手动指定内存大小,避免内存溢出:

storage:
  engine: inMemory
  dbPath: /data/mongodb/inmem
  inMemory:
    engineConfig:
      inMemorySizeGB: 4  # 限定内存占用4GB

四、MongoDB生产级环境搭建(双方式部署)

本文提供YUM源码部署Docker容器部署两种生产级方案,YUM部署适合物理机长期运维,Docker部署适合快速搭建、环境隔离、云服务器场景。

4.1 YUM方式部署MongoDB(CentOS7)

4.1.1 配置阿里云YUM源

官方源下载速度缓慢,采用阿里云镜像源提升部署速度,进入YUM配置目录,创建MongoDB专属源文件:

# 进入yum源配置目录
cd /etc/yum.repos.d
# 创建mongodb镜像源文件
vim mongodb-org-5.0.repo

写入以下镜像源配置内容:

[mongodb-org]
name=MongoDB Repository
baseurl=http://mirrors.aliyun.com/mongodb/yum/redhat/7Server/mongodb-org/5.0/x86_64/
gpgcheck=0
enabled=1

4.1.2 执行安装与环境初始化

# 更新yum缓存
yum update -y
# 安装MongoDB5.0版本全套组件
yum install -y mongodb-org
# 查看安装目录
whereis mongod

4.1.3 核心配置文件修改

编辑主配置文件,开启远程连接、优化性能参数:

vim /etc/mongod.conf

修改核心配置项:

systemLog:
  destination: file
  path: /var/log/mongodb/mongod.log
  logAppend: true
storage:
  dbPath: /var/lib/mongo
  journal:
    enabled: true
net:
  port: 27017
  bindIp: 0.0.0.0  # 允许所有IP远程连接,关闭仅本地访问
processManagement:
  fork: true  # 后台运行
security:
  authorization: disabled  # 初始关闭认证,后续按需开启

4.1.4 服务启停与开机自启

# 启动服务
systemctl start mongod.service
# 停止服务
systemctl stop mongod.service
# 重启服务
systemctl restart mongod.service
# 查看运行状态
systemctl status mongod.service
# 设置开机自启
systemctl enable mongod.service

4.2 Docker方式快速部署MongoDB

Docker部署无需复杂配置,环境隔离、一键搭建,适合测试、快速落地场景,默认部署5.0.9稳定版。

4.2.1 拉取镜像与创建挂载目录

# 拉取MongoDB镜像
docker pull mongo:5.0.9-focal
# 查看已下载镜像
docker images | grep mongo
# 创建数据持久化挂载目录
mkdir -p /mnt/mongodb/data /mnt/mongodb/log

4.2.2 启动容器服务

docker run -itd \
--privileged=true \
--name mongo5 \
-p 27017:27017 \
-v /mnt/mongodb/data:/data/db \
-v /mnt/mongodb/log:/var/log/mongodb \
--restart always \
mongo:5.0.9-focal

参数说明:端口映射27017(MongoDB默认端口)、数据日志持久化、开机自启、超级权限运行。

4.3 客户端连接测试

支持两种连接方式:命令行Shell连接、可视化工具连接,生产环境推荐使用NosqlBooster可视化工具,操作便捷。

# 本地shell直接连接
mongo
# 指定IP端口远程连接
mongo --host 127.0.0.1 --port 27017

五、MongoDB核心命令实操(CRUD+聚合+索引)

本章讲解企业开发高频核心命令,摒弃基础冗余语法,聚焦生产常用操作,附带完整实操案例与参数说明。

5.1 基础库与集合操作命令

# 1.查看所有数据库
show dbs
# 2.切换/创建数据库(不存在则自动创建)
use business_db
# 3.查看当前所在数据库
db
# 4.创建自定义集合
db.createCollection("user_info")
db.createCollection("order_list")
# 5.查看当前数据库所有集合
show collections
show tables
# 6.删除指定集合
db.user_info.drop()
# 7.删除当前整个数据库
db.dropDatabase()

5.2 文档CRUD增删改查实操

5.2.1 新增文档操作

集合不存在则自动创建,不指定_id时系统自动生成唯一ObjectId,支持单条、批量新增。

# 单条文档新增
db.user_info.insertOne({
  username: "wangwu",
  age: 26,
  gender: "male",
  isVip: false,
  score: 88.5,
  hobbies: ["篮球", "健身"],
  createTime: new Date()
})

# 批量文档新增
db.user_info.insertMany([
  {username: "zhaoliu", age: 29, gender: "female", isVip: true, score: 92.0, createTime: new Date()},
  {username: "tianqi", age: 24, gender: "male", isVip: true, score: 79.0, createTime: new Date()},
  {username: "luba", age: 30, gender: "female", isVip: false, score: 85.5, createTime: new Date()}
])

5.2.2 文档查询操作(高频)

支持全量查询、条件查询、比较查询、复合查询、排序分页,覆盖所有业务查询场景。

# 1.查询集合所有文档
db.user_info.find({})

# 2.条件精准查询(用户名匹配)
db.user_info.find({username: "wangwu"})

# 3.比较查询(年龄大于25岁)
db.user_info.find({age: {$gt: 25}})

# 4.复合条件查询(25-30岁的VIP用户)
db.user_info.find({
  age: {$gte:25, $lte:30},
  isVip: true
})

# 5.内嵌字段查询
db.user_info.find({"address.city": "深圳市"})

# 6.排序查询(按分数降序,年龄升序)
db.user_info.find({}).sort({score:-1, age:1})

# 7.分页查询(第1页,每页2条数据)
db.user_info.find({}).skip(0).limit(2)

# 8.只查询指定字段(只展示用户名、年龄)
db.user_info.find({}, {username:1, age:1, _id:0})

5.2.3 文档更新操作

支持单条更新、批量更新、字段新增、时间戳自动更新,规避全文档覆盖更新风险。

# 单条更新:修改指定用户分数,新增更新时间字段
db.user_info.updateOne(
  {username: "wangwu"},
  {
    $set: {score: 90.0, gender: "male"},
    $currentDate: {updateTime: true}
  }
)

# 批量更新:所有非VIP用户统一加分
db.user_info.updateMany(
  {isVip: false},
  {$inc: {score: 5.0}}
)

# 文档整体替换
db.user_info.replaceOne(
  {username: "luba"},
  {username: "luba", age: 31, isVip: true, score: 90.5, updateTime: new Date()}
)

5.2.4 文档删除操作

# 删除单条指定条件文档
db.user_info.deleteOne({username: "tianqi"})

# 批量删除指定条件文档
db.user_info.deleteMany({isVip: false})

# 清空集合所有文档(保留集合结构)
db.user_info.deleteMany({})

5.3 聚合管道高阶实操

聚合管道是MongoDB数据分析核心能力,通过多阶段流水线处理数据,实现分组统计、求和、平均值、排序筛选等复杂业务逻辑。

5.3.1 基础聚合统计

基于订单数据,实现按订单状态分组、统计订单数量、总金额、平均单价。

# 插入测试订单数据
db.order_list.insertMany([
  {orderNo:"ORD001", price:199, num:2, status:"已完成", createDate:ISODate("2025-05-01")},
  {orderNo:"ORD002", price:299, num:1, status:"待付款", createDate:ISODate("2025-05-02")},
  {orderNo:"ORD003", price:199, num:3, status:"已完成", createDate:ISODate("2025-05-03")},
  {orderNo:"ORD004", price:399, num:1, status:"已发货", createDate:ISODate("2025-05-04")},
  {orderNo:"ORD005", price:299, num:2, status:"待付款", createDate:ISODate("2025-05-05")}
])

# 聚合统计:按订单状态分组,统计订单数、总销售额、平均购买数量
db.order_list.aggregate([
  {$match: {createDate: {$gte: ISODate("2025-05-01")}}}, // 时间筛选
  {$group: {
    _id: "$status",
    totalOrder: {$sum: 1}, // 订单总数
    totalAmount: {$sum: {$multiply: ["$price", "$num"]}}, // 总金额
    avgNum: {$avg: "$num"} // 平均购买数量
  }},
  {$sort: {totalAmount: -1}} // 按总金额倒序
])

5.3.2 聚合管道优化规则

  • 优先使用$match前置过滤数据,减少后续管道处理的数据量,大幅提升性能;

  • project、project、projectaddFields后置执行,避免无效字段计算;

  • 单管道阶段数量不超过1000个,结果集大小不超过16MB,超大结果需分页处理。

5.4 全类型索引实操与优化

索引是数据库查询优化的核心,无索引查询会触发全表扫描,数据量越大性能差距越明显。MongoDB默认对_id字段创建唯一索引,支持多类型自定义索引。

5.4.1 单键索引

# 对用户年龄创建升序索引
db.user_info.createIndex({age:1})
# 对内嵌字段创建索引
db.user_info.createIndex({"address.city":1})

5.4.2 复合索引

# 联合索引:年龄+VIP状态
db.user_info.createIndex({age:1, isVip:-1})

5.4.3 多键索引(数组专用)

# 对爱好数组创建多键索引,优化数组查询
db.user_info.createIndex({hobbies:1})

5.4.4 地理空间索引(实战常用)

# 插入地理位置数据
db.store_info.insertOne({
  name: "深圳旗舰店",
  loc: {type: "Point", coordinates: [114.06, 22.54]}
})
# 创建球面地理索引
db.store_info.createIndex({loc: "2dsphere"})
# 查询指定范围内的门店
db.store_info.find({
  loc: {
    $geoWithin: {$center: [[114.06,22.54], 0.1]}
  }
})

5.4.5 索引管理命令

# 查看集合所有索引
db.user_info.getIndexes()
# 查看索引占用空间
db.user_info.totalIndexSize()
# 删除指定索引
db.user_info.dropIndex("age_1")
# 删除所有自定义索引(保留_id索引)
db.user_info.dropIndexes()

六、SpringBoot整合MongoDB企业级实战开发

SpringBoot整合MongoDB提供两种主流开发方式:MongoTemplate模板操作(灵活适配复杂SQL)、MongoRepository接口操作(简化CRUD),本章整合两种方式,封装企业级通用代码,适配生产开发。

6.1 项目依赖引入

SpringBoot2.7.x适配稳定依赖,无需额外冗余包,自动装配MongoDB核心配置。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.15</version>
        <relativePath/>
    </parent>

    <groupId>com.company.mongodb</groupId>
    <artifactId>mongodb-demo</artifactId>
    <version>1.0.0</version>
    <name>MongoDB企业级实战项目</name>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- SpringBoot Web核心 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- MongoDB自动装配依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

        <!-- 工具类 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

6.2 全局配置文件

配置MongoDB连接地址、数据库名称、连接池参数,优化高并发连接性能。

server:
  port: 8090
  servlet:
    context-path: /

# MongoDB集群配置
spring:
  data:
    mongodb:
      # 连接地址与数据库
      host: 127.0.0.1
      port: 27017
      database: business_db
      # 连接池配置
      pool:
        max-size: 100
        min-size: 10
        max-wait: 3000
        max-idle-time: 60000

# 日志配置
logging:
  level:
    com.company.mongodb: debug

6.3 实体类封装(文档映射)

创建用户实体类,映射MongoDB文档结构,适配嵌套文档、数组字段。

package com.company.mongodb.entity;

import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoId;

import java.util.Date;
import java.util.List;

/**
 * 用户文档实体类
 * 映射mongodb user_info集合
 */
@Data
@Document(collection = "user_info")
public class UserInfo {

    /**
     * 文档唯一主键
     */
    @MongoId
    private String id;

    /**
     * 用户名
     */
    @Field("username")
    private String username;

    /**
     * 年龄
     */
    @Field("age")
    private Integer age;

    /**
     * 性别
     */
    @Field("gender")
    private String gender;

    /**
     * 是否VIP
     */
    @Field("isVip")
    private Boolean isVip;

    /**
     * 积分
     */
    @Field("score")
    private Double score;

    /**
     * 爱好数组
     */
    @Field("hobbies")
    private List<String> hobbies;

    /**
     * 创建时间
     */
    @Field("createTime")
    private Date createTime;

    /**
     * 更新时间
     */
    @Field("updateTime")
    private Date updateTime;
}

6.4 Repository接口极简CRUD

继承MongoRepository接口,无需编写SQL,自动实现增删改查、条件查询、分页排序。

package com.company.mongodb.repository;

import com.company.mongodb.entity.UserInfo;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 用户数据访问接口
 * 继承MongoRepository实现极简CRUD
 */
@Repository
public interface UserInfoRepository extends MongoRepository<UserInfo, String> {

    /**
     * 根据用户名精准查询
     */
    UserInfo findByUsername(String username);

    /**
     * 根据VIP状态和年龄范围查询用户
     */
    List<UserInfo> findByIsVipAndAgeGreaterThan(Boolean isVip, Integer age);

    /**
     * 根据用户名删除用户
     */
    void deleteByUsername(String username);
}

6.5 MongoTemplate复杂操作工具类

封装聚合查询、复杂条件筛选、分页、批量更新、地理查询等高阶功能,适配复杂业务场景。

package com.company.mongodb.util;

import com.company.mongodb.entity.UserInfo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * MongoDB高阶操作工具类
 * 适配复杂查询、聚合统计、批量操作
 */
@Component
public class MongoDbUtil {

    @Resource
    private MongoTemplate mongoTemplate;

    /**
     * 分页条件查询用户
     */
    public Page<UserInfo> pageQueryUser(Integer pageNum, Integer pageSize, Boolean isVip, Integer minAge) {
        // 构建查询条件
        Criteria criteria = new Criteria();
        if (isVip != null) {
            criteria.and("isVip").is(isVip);
        }
        if (minAge != null) {
            criteria.and("age").gte(minAge);
        }
        Query query = new Query(criteria);
        // 分页参数
        PageRequest pageRequest = PageRequest.of(pageNum - 1, pageSize);
        query.with(pageRequest);
        // 查询数据与总条数
        List<UserInfo> userList = mongoTemplate.find(query, UserInfo.class);
        long total = mongoTemplate.count(query, UserInfo.class);
        return new PageImpl<>(userList, pageRequest, total);
    }

    /**
     * 批量更新用户积分
     */
    public void batchUpdateUserScore(Integer addScore, Boolean isVip) {
        Query query = new Query(Criteria.where("isVip").is(isVip));
        Update update = new Update().inc("score", addScore);
        mongoTemplate.updateMulti(query, update, UserInfo.class);
    }

    /**
     * 聚合统计VIP/非VIP用户数量与平均积分
     */
    public List<Map> aggregateUserStats() {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.group("isVip")
                        .count().as("userCount")
                        .avg("score").as("avgScore")
        );
        AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "user_info", Map.class);
        return results.getMappedResults();
    }
}

6.6 统一返回结果与控制层接口

6.6.1 全局统一返回实体

package com.company.mongodb.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 全局统一接口返回结果
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    private Integer code;
    private String msg;
    private T data;

    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }

    public static <T> Result<T> success() {
        return new Result<>(200, "操作成功", null);
    }

    public static <T> Result<T> fail(String msg) {
        return new Result<>(500, msg, null);
    }
}

6.6.2 业务控制层接口

package com.company.mongodb.controller;

import com.company.mongodb.entity.Result;
import com.company.mongodb.entity.UserInfo;
import com.company.mongodb.repository.UserInfoRepository;
import com.company.mongodb.util.MongoDbUtil;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * MongoDB业务接口控制层
 * 整合Repository简易操作 + MongoTemplate高阶操作
 */
@RestController
@RequestMapping("/mongodb/user")
public class UserController {

    @Resource
    private UserInfoRepository userInfoRepository;

    @Resource
    private MongoDbUtil mongoDbUtil;

    /**
     * 新增用户(Repository原生CRUD)
     */
    @GetMapping("/save")
    public Result<UserInfo> saveUser(UserInfo userInfo) {
        UserInfo save = userInfoRepository.save(userInfo);
        return Result.success(save);
    }

    /**
     * 根据用户名查询用户
     */
    @GetMapping("/getByUsername")
    public Result<UserInfo> getByUsername(@RequestParam String username) {
        UserInfo userInfo = userInfoRepository.findByUsername(username);
        return Result.success(userInfo);
    }

    /**
     * 分页条件查询用户(MongoTemplate高阶分页)
     */
    @GetMapping("/page")
    public Result<Page<UserInfo>> pageQueryUser(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "5") Integer pageSize,
            @RequestParam(required = false) Boolean isVip,
            @RequestParam(required = false) Integer minAge) {
        Page<UserInfo> pageResult = mongoDbUtil.pageQueryUser(pageNum, pageSize, isVip, minAge);
        return Result.success(pageResult);
    }

    /**
     * 批量更新用户积分
     */
    @GetMapping("/batchUpdateScore")
    public Result<String> batchUpdateScore(
            @RequestParam Integer addScore,
            @RequestParam Boolean isVip) {
        mongoDbUtil.batchUpdateUserScore(addScore, isVip);
        return Result.success("批量更新用户积分成功");
    }

    /**
     * 聚合统计VIP用户数据
     */
    @GetMapping("/stats")
    public Result<List<Map>> userStats() {
        List<Map> stats = mongoDbUtil.aggregateUserStats();
        return Result.success(stats);
    }

    /**
     * 删除用户
     */
    @GetMapping("/delete")
    public Result<String> deleteUser(@RequestParam String username) {
        userInfoRepository.deleteByUsername(username);
        return Result.success("用户删除成功");
    }
}

6.7 项目启动与接口测试

6.7.1 启动类编写

创建项目启动入口类,开启SpringBoot自动装配,无需额外配置即可整合MongoDB:

package com.company.mongodb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * MongoDB实战项目启动类
 */
@SpringBootApplication
public class MongodbApplication {
    public static void main(String[] args) {
        SpringApplication.run(MongodbApplication.class, args);
    }
}

6.7.2 接口测试方式

项目启动成功后(默认端口8090),可通过Postman、浏览器、Apifox测试所有接口,核心测试接口如下:

  • 新增用户http://localhost:8090/mongodb/user/save(GET传参)

  • 用户名查询http://localhost:8090/mongodb/user/getByUsername?username=wangwu

  • 分页查询http://localhost:8090/mongodb/user/page?pageNum=1&pageSize=5&isVip=true&minAge=25

  • 批量积分更新http://localhost:8090/mongodb/user/batchUpdateScore?addScore=10&isVip=true

  • 用户数据聚合统计http://localhost:8090/mongodb/user/stats

  • 删除用户http://localhost:8090/mongodb/user/delete?username=tianqi

七、MongoDB生产调优与避坑指南

7.1 高频性能优化方案

7.1.1 索引优化核心准则

  • 禁止无索引全表扫描,高频查询字段必须建立索引,查询条件字段优先建立单键/复合索引;

  • 复合索引遵循等值在前、范围在后原则,例如:精准匹配字段+排序/范围字段;

  • 避免创建冗余索引,重复字段索引会增加写入开销,定期清理无效索引;

  • 数组字段查询必须使用多键索引,大幅提升数组匹配、筛选性能。

7.1.2 查询语句优化

  • 查询时按需返回字段,通过字段:1/_id:0屏蔽无效字段,减少数据传输开销;

  • 聚合管道优先前置$match过滤数据,减少后续流水线数据处理量;

  • 大数据量查询必须分页,避免单次查询超16MB默认结果集上限;

  • 禁止使用模糊全匹配查询(/xxx/),无法走索引,海量数据下性能极差。

7.1.3 服务配置调优

  • 合理配置WiredTiger缓存,生产环境内存16G以上服务器,默认缓存配置可满足绝大多数场景;

  • 开启日志轮转,避免日志文件过大占用磁盘,定期清理历史冗余日志;

  • 连接池参数适配业务并发量,高并发场景调大最大连接数,避免连接超时阻塞。

7.2 企业开发常见坑点与解决方案

  • 坑点1:字段类型不匹配导致查询失效
    问题:数据库字段为数值型,代码传入字符串参数,索引失效查询无结果;
    解决方案:统一代码与数据库字段类型,数值、字符串、日期类型严格对应。

  • 坑点2:无事务导致数据一致性问题
    问题:多文档操作中途报错,出现数据脏数据、数据不一致;
    解决方案:MongoDB4.0+支持副本集事务、4.2+支持分片事务,多文档更新开启事务机制。

  • 坑点3:嵌套层级过深导致查询卡顿
    问题:文档嵌套层级超过5层,解析、查询性能大幅下降;
    解决方案:复杂关联场景改用引用模型,控制嵌套层级在3层以内。

  • 坑点4:批量操作无过滤条件引发全表更新/删除
    问题:误执行无参数updateMany、deleteMany,导致全表数据篡改或丢失;
    解决方案:生产环境禁止空条件批量操作,代码层增加参数校验拦截。

  • 坑点5:索引过多影响写入性能
    问题:集合索引数量过多,新增、修改数据需同步更新所有索引,导致写入卡顿;
    解决方案:仅为查询、排序、筛选字段建索引,单集合索引数量不超过8个。

八、全文总结

本教程从理论、原理、部署、命令实操、代码整合、生产调优六个维度,完整覆盖MongoDB企业级落地全流程。核心内容总结如下:

1、MongoDB作为文档型NoSQL数据库,核心优势为无模式灵活存储、高并发读写、弹性扩容、适配非结构化数据,完美适配互联网快速迭代、海量数据、高并发业务场景。

2、核心数据模型依托BSON格式,通过内嵌文档、引用关联两种模型适配不同业务关联场景,规避关系型数据库多表JOIN性能短板。

3、生产环境优先使用WiredTiger存储引擎,依托文档级锁、MVCC机制、数据压缩能力,保障高并发、高可用、高存储效率。

4、支持YUM物理机、Docker容器两种生产部署方式,适配不同服务器环境,部署简单、运维成本低。

5、核心CRUD、聚合管道、多类型索引是业务开发核心,熟练掌握高阶语法可实现绝大部分数据分析、数据查询场景。

6、SpringBoot双方式整合(Repository+MongoTemplate),兼顾开发效率与业务灵活性,可直接复用至企业项目开发。

7、生产开发需严格遵循索引优化、查询优化规范,规避常见坑点,保障数据库高性能、高稳定运行。

更多推荐