MongoDB 是目前最流行的文档型 NoSQL 数据库,因灵活的 schema、高扩展性和适用于非结构化 / 半结构化数据场景而被广泛使用。以下是关于 MongoDB 的核心知识点及常见面试题:
一、基础概念与特性
-
什么是 MongoDB?它的核心特性是什么?
- 定义:MongoDB 是一个开源的文档型 NoSQL 数据库,数据以类似 JSON 的 "文档"(BSON 格式)存储,无需预定义 schema(表结构),适合存储非结构化或半结构化数据。
- 核心特性:
- 文档模型:数据以键值对形式的文档(BSON)存储,支持嵌套结构(如嵌入子文档、数组),更贴近业务对象(如用户文档可直接包含地址、订单列表)。
- 无 schema 设计:同一集合(Collection)中的文档可拥有不同字段和结构,适合快速迭代的业务(如电商商品属性多变场景)。
- 高可扩展性:支持副本集(高可用)和分片(水平扩展),轻松应对海量数据和高并发。
- 丰富的查询能力:支持复杂查询(如范围查询、正则匹配)、聚合管道(数据分析)、地理空间查询等。
- 索引支持:类似关系型数据库,支持单字段、复合、地理空间等多种索引,提升查询效率。
-
MongoDB 与关系型数据库(如 MySQL)的核心区别是什么?
维度 MongoDB(文档型) 关系型数据库(如 MySQL) 数据模型 文档(BSON),支持嵌套和数组 二维表(行 + 列),需预定义表结构 Schema 动态 schema(同一集合文档结构可不同) 固定 schema(表结构需提前定义,修改成本高) 关联关系 推荐通过 "嵌入文档" 减少关联,也支持引用($lookup) 通过外键强关联(JOIN 操作) 事务支持 4.0+ 支持多文档事务(有限制) 完整 ACID 事务支持 适用场景 非结构化数据(日志、用户画像)、快速迭代业务 结构化数据(订单、财务)、强事务场景 -
MongoDB 中的 "文档(Document)""集合(Collection)""数据库(Database)" 分别对应关系型数据库的什么概念?
-
文档(Document) :类似关系型数据库的 "行(Row)",但结构更灵活(可嵌套),以 BSON(二进制 JSON)格式存储(支持更多数据类型,如日期、ObjectId)。例:一个用户文档
json
{ "_id": ObjectId("650a1b3d7f82e1234567890"), // 自动生成的唯一标识 "name": "张三", "age": 25, "address": { "city": "北京", "street": "XX路" }, // 嵌入子文档 "hobbies": ["篮球", "游戏"] // 数组 }
-
集合(Collection):类似关系型数据库的 "表(Table)",是文档的集合,但集合中文档的结构可以不同(无 schema 约束)。
-
数据库(Database) :类似关系型数据库的 "数据库",是集合的容器,一个 MongoDB 实例可包含多个数据库(如
user_db
、order_db
)。
-
-
MongoDB 中的
_id
字段有什么作用?_id
是文档的唯一标识 ,类似关系型数据库的主键(Primary Key),每个文档必须包含_id
字段(若未手动指定,MongoDB 会自动生成)。- 自动生成的
_id
为ObjectId
类型(12 字节),包含时间戳、机器 ID、进程 ID、自增计数器,确保分布式环境下的唯一性。 - 可手动指定
_id
(如用业务 ID 作为_id
),但需保证唯一性,否则插入会报错(duplicate key error
)。
二、数据类型与核心操作
-
**MongoDB 支持哪些特殊数据类型?**除了常见的字符串(String)、数字(Int/Double)、布尔(Boolean),还支持:
ObjectId
:用于_id
字段,确保文档唯一性。Date
:日期时间(精确到毫秒),如ISODate("2023-09-20T08:00:00Z")
。Array
:数组,可包含多个值(如[1, 2, 3]
或["a", {b:1}]
)。Embedded Document
:嵌入子文档(如{address: {city: "上海"}}
)。Binary Data
:二进制数据(如图片、文件)。Regular Expression
:正则表达式(用于模糊查询)。
-
MongoDB 的 CRUD 操作常用命令是什么?
-
创建(Create) :
insertOne()
(单文档)、insertMany()
(多文档)javascript
运行
db.users.insertOne({ name: "李四", age: 30 }) // 插入单条 db.users.insertMany([{ name: "王五" }, { name: "赵六" }]) // 插入多条
-
查询(Read) :
find()
(查询所有)、findOne()
(查询单条),支持条件筛选(query
)和投影(projection
)javascript
运行
// 查询年龄>25的用户,只返回name和age字段(_id默认返回,需显式排除) db.users.find({ age: { $gt: 25 } }, { name: 1, age: 1, _id: 0 })
-
更新(Update) :
updateOne()
(更新匹配的第一条)、updateMany()
(更新所有匹配),需用更新操作符($set
、$inc
等)javascript
运行
// 将name=李四的用户年龄+1 db.users.updateOne({ name: "李四" }, { $inc: { age: 1 } })
-
删除(Delete) :
deleteOne()
(删除匹配的第一条)、deleteMany()
(删除所有匹配)javascript
运行
// 删除年龄<18的用户 db.users.deleteMany({ age: { $lt: 18 } })
-
-
MongoDB 常用的查询操作符有哪些?
- 比较操作符:
$eq
(等于)、$gt
(大于)、$lt
(小于)、$gte
(大于等于)、$lte
(小于等于)、$ne
(不等于)、$in
(在数组中)、$nin
(不在数组中)。例:db.users.find({ age: { $gte: 18, $lte: 30 } })
(查询 18-30 岁用户)。 - 逻辑操作符:
$and
、$or
、$not
。例:db.users.find({ $or: [{ age: { $gt: 30 } }, { name: "张三" }] })
。 - 数组操作符:
$in
(数组包含任一元素)、$all
(数组包含所有元素)、$size
(数组长度)。例:db.users.find({ hobbies: { $all: ["篮球", "游戏"] } })
(爱好同时包含篮球和游戏)。 - 嵌入文档操作符:
$
(匹配数组中的元素)、$elemMatch
(数组元素多条件匹配)。
- 比较操作符:
-
什么是 MongoDB 聚合管道(Aggregation Pipeline)?举例说明其用途。
-
聚合管道是 MongoDB 用于复杂数据处理和分析的工具,由多个 "阶段(Stage)" 组成,文档按顺序经过每个阶段处理,最终输出结果(类似流水线)。
-
常用阶段:
$match
:筛选文档(类似find
的查询条件)。$group
:按字段分组,结合聚合函数($sum
、$avg
等)。$project
:修改文档结构(保留 / 排除字段、重命名)。$sort
:排序。$limit
:限制返回数量。
-
示例:统计每个城市的用户数量,并按数量降序排列
javascript
运行
db.users.aggregate([ { $match: { age: { $gte: 18 } } }, // 先筛选成年用户 { $group: { _id: "$address.city", count: { $sum: 1 } } }, // 按城市分组计数 { $sort: { count: -1 } }, // 按数量降序 { $limit: 10 } // 取前10 ])
-
三、索引与性能优化
-
**MongoDB 支持哪些类型的索引?**索引用于加速查询,避免全集合扫描(类似关系型数据库),常见类型:
- 单字段索引 :对单个字段创建索引,如
db.users.createIndex({ name: 1 })
(1 为升序,-1 为降序)。 - 复合索引 :对多个字段创建索引,遵循 "最左前缀原则",如
db.users.createIndex({ age: 1, name: -1 })
(查询age
或age+name
可命中,单独查name
不命中)。 - 地理空间索引 :用于地理位置查询(如附近的人),如
db.shops.createIndex({ location: "2dsphere" })
。 - 文本索引 :用于全文搜索,如
db.articles.createIndex({ content: "text" })
,支持$text
查询。 - 哈希索引:对字段哈希值创建索引,适合等值查询,不支持范围查询。
- 单字段索引 :对单个字段创建索引,如
-
如何查看索引是否被查询使用?如何优化索引?
-
用
explain()
分析查询执行计划,重点看executionStats.executionStages
中的stage
字段:IXSCAN
:索引扫描(命中索引,高效);COLLSCAN
:全集合扫描(未命中索引,需优化)。例:db.users.find({ age: { $gt: 25 } }).explain("executionStats")
。
-
索引优化原则:
- 只为高频查询字段创建索引(索引会增加写入 / 更新开销)。
- 复合索引字段顺序:将过滤性强的字段放前面(如性别字段过滤性差,不适合放前面)。
- 避免过度索引(一个集合索引建议不超过 5-10 个)。
- 删除未使用的索引(用
db.collection.getIndexes()
查看所有索引,结合监控判断是否有用)。
-
四、高可用与分布式
-
什么是 MongoDB 副本集(Replica Set)?它的作用是什么?
-
副本集是 MongoDB 实现高可用的核心机制,由多个 MongoDB 实例组成(通常 3 个节点),包含:
- Primary(主节点):唯一可接收写操作的节点,负责将数据同步到从节点。
- Secondary(从节点) :从主节点复制数据,仅处理读操作(可通过
readPreference
配置读策略)。 - Arbiter(仲裁节点):不存储数据,仅在主节点故障时参与投票选举新主节点(减少资源占用)。
-
作用:
- 故障转移:主节点宕机后,副本集会自动选举新主节点(通过 Raft 算法),确保服务不中断。
- 读写分离:读操作可分流到从节点,减轻主节点压力。
- 数据冗余:数据多副本存储,避免单点故障导致数据丢失。
-
-
副本集的故障转移过程是怎样的?
-
- 检测故障 :从节点定期向主节点发送心跳(默认每 2 秒),若超过
electionTimeoutMillis
(默认 10 秒)未收到响应,判定主节点不可用。
- 检测故障 :从节点定期向主节点发送心跳(默认每 2 秒),若超过
-
- 发起选举:一个从节点(优先级高的优先)发起选举,向其他节点请求投票。
-
- 选举新主节点:获得多数节点(超过半数)投票的从节点成为新主节点(如 3 节点副本集需至少 2 票)。
-
- 同步数据:新主节点接收写操作,其他从节点从新主节点同步数据,恢复正常服务。
-
-
什么是 MongoDB 分片(Sharding)?为什么需要分片?
-
分片是 MongoDB 应对海量数据的水平扩展方案,将大集合的数据拆分到多个 "分片(Shard)"(每个分片是一个副本集),实现数据分布式存储。
-
为什么需要分片?
- 单节点存储容量有限(如磁盘空间不足);
- 单节点读写压力过大(无法通过副本集分担,因副本集数据全量复制)。
-
分片集群的核心组件:
- Shard(分片):存储实际数据(每个分片是副本集,保证高可用)。
- Mongos(路由节点):接收客户端请求,根据分片键将请求路由到对应的分片(客户端只连接 Mongos)。
- Config Server(配置服务器):存储集群元数据(如分片键范围、分片映射关系),通常为 3 节点副本集。
-
-
分片键(Shard Key)的作用是什么?选择分片键需注意什么?
-
分片键是用于将集合数据拆分到不同分片的字段(如
user_id
、create_time
),MongoDB 通过分片键的值范围或哈希值决定数据存储的分片。 -
选择原则:
- 均衡性:分片键需使数据均匀分布到所有分片(避免某分片数据过多,成为瓶颈)。反例:用性别(男 / 女)作为分片键,仅能拆分到 2 个分片,无法扩展。
- 查询效率:分片键需与高频查询字段匹配(避免 Mongos 向所有分片广播查询)。
- 不可变:分片键一旦设置,字段值不可修改(否则数据无法正确路由)。
-
五、事务与数据一致性
-
MongoDB 支持事务吗?有什么限制?
- 支持:MongoDB 4.0 开始支持多文档事务(单文档事务天然支持),4.2 后支持分片集群中的事务。
- 特性:满足 ACID 特性(原子性、一致性、隔离性、持久性),类似关系型数据库事务。
- 限制:
- 事务中的写操作会加锁,可能影响性能(建议事务执行时间短)。
- 分片集群中,事务涉及的文档需在同一个分片(通过分片键保证),否则事务失败。
- 不支持跨数据库的事务(4.2+ 支持同一部署下的跨库事务)。
-
如何保证 MongoDB 数据的一致性?
- 副本集同步:主节点写入后,数据通过 oplog(操作日志)同步到从节点,确保从节点数据最终一致(默认异步同步,可配置 "多数确认" 保证强一致性)。
- 事务:多文档操作通过事务保证原子性(要么全成功,要么全失败)。
- 读写策略:
- 写操作:通过
writeConcern
配置(如{ w: "majority" }
表示等待多数节点确认后返回,确保数据不丢失)。 - 读操作:通过
readPreference
配置(如primary
只从主节点读,保证最新;secondaryPreferred
优先从从节点读,提升性能)。
- 写操作:通过
六、适用场景与局限性
- MongoDB 适合哪些场景?不适合哪些场景?
-
适合场景:
- 非结构化 / 半结构化数据(如日志、用户画像、JSON 格式数据);
- 快速迭代的业务(无需频繁修改 schema);
- 高写入吞吐量场景(如物联网设备数据上报);
- 地理空间查询(如 LBS 应用:附近的商家);
- 大数据量存储(通过分片扩展)。
-
不适合场景:
- 强事务需求(如金融交易,虽支持事务,但关系型数据库更成熟);
- 复杂多表关联查询(MongoDB 的
$lookup
性能不如关系型数据库的 JOIN); - 数据一致性要求极高(如库存管理,需频繁锁操作)。
-