外观
MongoDB 逻辑架构
MongoDB 的逻辑架构是其数据组织和管理的核心框架,从最基本的文档(Document)到复杂的分片集群(Sharded Cluster),构成了一个层次化的数据管理体系。理解 MongoDB 的逻辑架构对于设计高效、可扩展的数据库系统至关重要。
文档(Document)
文档是 MongoDB 中数据的基本单位,采用 BSON(Binary JSON)格式存储,类似于关系型数据库中的行,但具有更大的灵活性。
文档特点
- 自包含:每个文档包含其所有字段和值,无需跨表关联
- 动态模式:同一个集合中的文档可以有不同的字段结构
- 丰富的数据类型:支持字符串、数字、布尔值、日期、数组、嵌套文档等多种数据类型
- 唯一标识符:每个文档自动分配一个
_id字段作为主键
文档示例
json
{
"_id": ObjectId("60f7b3a4c9e7d8e6f7a8b9c0"),
"name": "MongoDB 逻辑架构",
"author": "MongoDB Team",
"tags": ["MongoDB", "Architecture", "Database"],
"created_at": ISODate("2023-07-21T10:00:00Z"),
"updated_at": ISODate("2023-07-21T11:30:00Z"),
"metadata": {
"version": "1.0",
"category": "Technical"
}
}集合(Collection)
集合是文档的容器,类似于关系型数据库中的表,但没有固定的 schema(模式)。
集合特点
- 无模式约束:集合中的文档可以有不同的字段结构
- 动态扩展:可以随时向集合中添加新的文档,无需预先定义结构
- 命名规则:集合名称不能包含特殊字符,不能以 "system." 开头(系统集合保留)
- 命名空间:集合的完整名称为
database.collection,例如mydb.users
集合类型
- 普通集合:存储用户数据的标准集合
- 固定集合(Capped Collection):具有固定大小的集合,当达到最大大小时会自动覆盖最旧的文档
- 系统集合:MongoDB 内部使用的集合,名称以 "system." 开头
- 时间序列集合:专门用于存储时间序列数据的优化集合
数据库(Database)
数据库是集合的容器,类似于关系型数据库中的数据库。每个 MongoDB 实例可以包含多个数据库。
数据库特点
- 独立命名空间:不同数据库中的集合名称可以重复
- 独立权限控制:可以为每个数据库设置不同的用户权限
- 独立存储文件:每个数据库的数据存储在独立的文件中
- 默认数据库:如果不指定数据库,MongoDB 会使用 "test" 数据库
数据库命名规则
- 不能是空字符串
- 不能包含特殊字符(/."$*<>:|?)
- 区分大小写
- 建议使用小写字母
- 不能超过 64 字节
系统数据库
- admin:存储管理用户和权限信息
- local:存储副本集和分片集群的本地信息,不会被复制
- config:存储分片集群的配置信息
索引(Index)
索引是提高查询性能的关键组件,类似于关系型数据库中的索引。
索引特点
- 基于 B 树结构:支持高效的范围查询和排序
- 多种索引类型:支持单字段索引、复合索引、多键索引、地理空间索引等
- 自动创建:MongoDB 会自动为
_id字段创建唯一索引 - 查询优化:查询优化器会自动选择合适的索引
索引类型
| 索引类型 | 描述 | 适用场景 |
|---|---|---|
| 单字段索引 | 基于单个字段创建的索引 | 频繁基于单个字段查询 |
| 复合索引 | 基于多个字段创建的索引 | 频繁基于多个字段组合查询 |
| 多键索引 | 为数组字段创建的索引 | 频繁查询数组中的元素 |
| 地理空间索引 | 为地理空间数据创建的索引 | 位置查询、地理围栏等 |
| 文本索引 | 为文本内容创建的索引 | 全文搜索 |
| 哈希索引 | 基于字段哈希值创建的索引 | 等值查询,适合分片键 |
| 唯一索引 | 确保索引字段的值唯一 | 确保数据唯一性,如用户名 |
命名空间(Namespace)
命名空间是 MongoDB 中用于标识集合的完整路径,格式为 database.collection。
命名空间特点
- 唯一标识:每个集合有一个唯一的命名空间
- 存储结构:每个命名空间对应一个数据文件(.ns 文件)
- 大小限制:命名空间的最大长度为 128 字节
- 系统命名空间:以 "system." 开头的命名空间用于 MongoDB 内部使用
分片集群(Sharded Cluster)逻辑架构
分片集群是 MongoDB 用于处理大规模数据的分布式架构,由以下组件组成:
1. 分片(Shard)
分片是存储数据的物理节点,通常是一个复制集,用于处理数据的读写操作。
- 数据分布:每个分片存储集群数据的一部分
- 复制保障:作为复制集,提供数据冗余和高可用性
- 水平扩展:可以通过添加分片来扩展集群的存储和处理能力
2. 路由服务器(mongos)
mongos 是客户端与分片集群之间的中间层,负责路由请求和聚合结果。
- 请求路由:根据分片键将客户端请求路由到相应的分片
- 结果聚合:将来自多个分片的查询结果聚合后返回给客户端
- 元数据缓存:缓存分片集群的元数据,提高路由效率
- 负载均衡:多个 mongos 节点可以提供负载均衡
3. 配置服务器(Config Server)
配置服务器存储分片集群的元数据和配置信息,通常是一个复制集。
- 元数据存储:存储分片信息、数据库和集合的分片配置、块分布等
- 一致性保障:作为复制集,确保元数据的一致性和可用性
- 版本控制:跟踪分片集群配置的变更历史
4. 分片键(Shard Key)
分片键是决定数据如何分布到各个分片的字段或字段组合。
- 选择原则:
- 高 cardinality(高基数):具有大量不同值
- 均匀分布:数据能均匀分布到各个分片
- 查询模式匹配:与常见查询模式匹配,减少跨分片查询
- 不可更改:集合分片后,分片键不能更改
- 复合分片键:可以使用多个字段组合作为分片键
5. 块(Chunk)
块是分片数据的基本单位,每个块包含一定范围的分片键值。
- 默认大小:默认块大小为 64MB
- 动态拆分:当块大小超过阈值时,会自动拆分为两个较小的块
- 均衡分布:均衡器会自动将块迁移到负载较轻的分片
- 块边界:每个块有一个最小和最大分片键值,定义其数据范围
数据分布逻辑
分片集群的数据分布
数据写入:
- 客户端将写请求发送到 mongos
- mongos 根据分片键确定目标分片
- mongos 将请求路由到相应的分片
- 分片处理写请求并返回结果
数据读取:
- 客户端将读请求发送到 mongos
- mongos 根据分片键确定涉及的分片
- mongos 并行向相关分片发送请求
- 分片返回结果,mongos 聚合后返回给客户端
块管理:
- 当块大小超过阈值时,自动拆分
- 均衡器监控块分布,自动迁移块
- 确保数据均匀分布到各个分片
数据一致性模型
复制集一致性:
- 默认使用 "多数派确认" 写入策略
- 支持读取关注点(Read Concern)和写入关注点(Write Concern)
- 可以根据需求调整一致性级别
分片集群一致性:
- 依赖复制集的一致性保障
- 配置服务器使用多数派写入确保元数据一致性
- mongos 缓存元数据,可能存在短暂的不一致
逻辑架构设计最佳实践
1. 文档设计原则
- 嵌入式数据模型优先:将相关数据嵌入到同一文档中,减少跨文档查询
- 合适的文档大小:单个文档大小不超过 16MB
- 避免过度嵌套:嵌套层级不宜过深,影响查询性能
- 使用数组存储多值字段:适合存储列表数据
2. 集合设计原则
- 按业务领域划分:将相关文档组织到同一集合
- 考虑查询模式:根据常见查询模式设计集合结构
- 避免大集合:对于超大规模数据,考虑分片
- 合理使用固定集合:适合存储日志、事件等时序数据
3. 索引设计原则
- 为频繁查询的字段创建索引:提高查询性能
- 选择合适的索引类型:根据查询类型选择最适合的索引
- 控制索引数量:索引会增加写入开销,不宜过多
- 定期优化索引:删除不使用的索引
4. 分片设计原则
- 选择合适的分片键:考虑基数、分布均匀性和查询模式
- 预分割数据:在分片初期预分割数据,避免热点问题
- 监控块分布:确保数据均匀分布到各个分片
- 合理设置块大小:根据业务需求调整块大小
常见问题与解决方案
问题:文档大小超过 16MB 限制
可能原因:
- 单个文档包含大量数据
- 嵌套层级过深
- 数组字段包含过多元素
解决方案:
- 重构文档结构,将大文档拆分为多个关联文档
- 使用 GridFS 存储大型文件
- 优化数据模型,减少不必要的字段
问题:查询性能不佳
可能原因:
- 缺少合适的索引
- 分片键选择不当
- 查询条件过于复杂
解决方案:
- 为频繁查询的字段创建索引
- 分析查询计划,优化查询条件
- 调整分片键设计
问题:数据分布不均匀
可能原因:
- 分片键选择不当
- 写入模式导致热点分片
- 均衡器未正常工作
解决方案:
- 重新评估分片键设计
- 调整写入模式,避免热点
- 检查均衡器状态,手动触发均衡
常见问题(FAQ)
Q1: MongoDB 的文档和关系型数据库的行有什么区别?
A1: MongoDB 的文档是自包含的数据单元,采用 BSON 格式,可以包含嵌套文档和数组,具有动态模式;而关系型数据库的行是基于固定模式的,数据分散在多个表中,需要通过 JOIN 操作关联。
Q2: 什么时候应该使用固定集合?
A2: 固定集合适合存储日志、事件记录、传感器数据等时序数据,以及需要自动覆盖旧数据的场景。固定集合的插入性能较高,支持按插入顺序查询。
Q3: 如何选择合适的分片键?
A3: 选择分片键时应考虑:高基数(大量不同值)、均匀分布(数据能均匀分布到各个分片)、与查询模式匹配(减少跨分片查询)。避免使用单调递增/递减的字段作为分片键,这会导致热点问题。
Q4: MongoDB 支持事务吗?
A4: MongoDB 4.0+ 支持多文档事务,包括复制集和分片集群环境。事务可以确保多个操作的原子性,适合需要跨文档一致性的场景。
Q5: 如何优化 MongoDB 的写入性能?
A5: 优化写入性能的方法包括:使用批量写入、调整写入关注点、优化索引(减少索引数量)、使用合适的存储引擎、调整 WiredTiger 缓存大小、考虑分片集群等。
