MongoDB介绍
什么是MongoDB,主要是做什么的
NoSQL的内容在这里就不展开说了,我相信看到这个的大家都知道什么是 NoSQL
MongoDB 是一款开源的分布式文档型 NoSQL 数据库,由 MongoDB Inc. 开发维护,由C++语言编写,核心是存储 “文档” 而非传统关系型数据库的 “行 - 列” 数据,旨在解决海量数据存储、高并发读写、灵活 schema 等传统数据库难以高效应对的问题。
作为基于分布式文件存储的数据库,它天生为处理大规模数据设计,目前是 NoSQL 数据库中市场份额最高、生态最完善的产品之一。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
它支持的数据结构非常松散,是类似 Json的 bson 格式,因此可以存储比较复杂的数据类型。
Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引
MongoDB的记录是一个文档,由一个字段和值对组合而成的数据结构,MongoDB文档类似于 JSON 对象,也就是MongoDB认为一个文档就是一个对象,字段的值除了基本数据类型意外,还可以包括其他文档
一般情况下,在如下场景优先考虑 MongoDB:
- 数据结构多变的场景:如用户画像(用户可能有 “手机号”“邮箱”“社交账号” 等不确定字段)、商品属性(不同品类商品属性差异大,如 “手机” 有 “内存”,“衣服” 有 “尺码”)。
- 海量数据存储与高并发读写场景:如系统日志(微服务产生的大量日志需高效写入和查询)、用户行为数据(如浏览记录、点击轨迹)。
- 需要灵活查询与统计的场景:如运营报表(按时间、地区、用户等级多维度统计数据),可通过 Aggregation Pipeline 高效实现。
- 分布式微服务架构场景:MongoDB 的分片、副本集特性可与微服务的水平扩展能力匹配,避免数据库成为瓶颈。
这些应用场景中,数据操作方面的共同特点是:
- 数据量大
- 写入频繁
- 价值较低
- 事务性弱
不适合的情况还是那样,和大部分 NoSQL 类似,不适合需要强事务(如金融交易的 “转账” 操作,需 ACID 严格保证)、多表复杂关联查询(如多表 JOIN)的场景,仍建议用关系型数据库。
MongoDB核心概念
假如我们对 MySQL 概念已非常熟悉,那我们可以通过对比快速理解 MongoDB 核心概念:
| 对比维度 | MongoDB(文档型) | MySQL(关系型) | 核心说明 |
|---|---|---|---|
| 数据库 | Database(数据库) | Database(数据库) | 概念完全一致,一个项目对应一个 Database(如 ZhiyanPlatform 对应
zhiyan_db)。 |
| 表 | Collection(集合) | Table(表) | Collection 是文档的 “容器”,无需预先定义结构(如 user
集合可存储不同字段的用户文档)。 |
| 行 | Document(文档) | Row(行) | 一个 Document 对应一条数据,格式为 BSON(如
{ "_id": 1, "name": "张三", "age": 25 })。 |
| 列 | Field(字段) | Column(列) | 一个 Document 中的键值对即为 Field,同一 Collection 中不同 Document 的 Field 可不同。 |
| 主键 | _id 字段(默认自动生成) |
Primary Key(需手动指定) | MongoDB 会为每个 Document 自动生成唯一的 _id(ObjectId
类型,12 字节,包含时间戳、机器 ID 等),也可手动指定(如用用户 ID 作为
_id)。 |
| 关联 | 嵌入式文档 / 引用 | Foreign Key(外键) | MongoDB 不支持外键,关联数据可通过 “嵌入式”(如用户文档内嵌地址)或
“引用”(如订单文档存储用户
_id)实现,更灵活但需手动维护一致性。 |
在你的电脑搭建MongoDB
访问 MongoDB 官网,选择 Windows 系统对应的安装包(建议下载 6.x 或 7.x 稳定版)。
安装MongoDB
- 双击安装包,可以勾选 “Install MongoDB Compass”(可视化工具,方便查看数据);
接下来就是正常安装,MongoDB没什么特殊的
- 按下
Win + R,输入services.msc,找到 “MongoDB Server (MongoDB)”; - 右键 “启动”,确认服务状态为 “正在运行”(默认端口 27017)。
接着验证连接
- 打开 MongoDB Compass,默认连接地址为
mongodb://localhost:27017/,点击 “Connect”; - 连接成功后,可看到默认数据库,至此本地环境搭建完成。
这个是我之前项目的连接
MongoDB的数据存储结构
逻辑存储结构
MongoDB 的数据存储结构遵循 “逻辑层 → 物理层” 的分层设计
逻辑结构是 MongoDB 对外暴露的核心概念,也是开发者日常使用(如创建集合、插入文档)时直接接触的层级,自上而下分为 数据库(Database)→ 集合(Collection)→ 文档(Document)→ 字段(Field) 四级,层级关系如下:
1 | 数据库(Database) |
文档(document)是MongoDB中数据的基本存储单元,类似与关系型数据库管理系统中的行
集合(collection)可以看作是一个拥有动态模式(dynamic schema)的表。
MongoDB 的一个实例可以拥有多个相互独立的数据库(database),每一个数据库都拥有自己的集合。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档是类似于 JSON 对象的 BSON 对象。字段值可以包含其他文档,数组及文档数组。
数据库
数据库是MongoDB 中最高级别的数据容器,对应 “一个独立的业务系统”(如 “电商数据库”“日志数据库”),用于隔离不同业务的数据。
在MongoDB中,多个文档组成集合,而多个集合可以组成数据库,一个MongoDB实例可以建立多个数据库。
MongoDB 的默认数据库为”db”,该数据库存储在data目录中。
MongoDB 启动后默认提供 3 个系统数据库,不可删除:
admin:权限数据库,存储用户账号和角色信息,拥有最高权限。local:本地数据库,存储当前 MongoDB 节点的本地数据(如副本集配置),不会同步到其他节点。config:分片集群专用数据库,存储分片集群的元数据(如分片节点信息、数据分布规则)。
而且 MongoDB
的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。底层存储上,每个数据库对应磁盘中一个独立的
“目录”(目录名即数据库名,如 mydb 数据库对应
mydb.ns
等文件所在的目录)。每个数据库有独立的权限控制(如仅允许 “电商用户”
访问电商数据库)。
数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。
- 不能是空字符串(““)。
- 不得含有’ ’(空格)、.、$、/、\0 (空字符)。
- 应全部小写。
- 最多64字节。
和关系型数据库类似,“show dbs” 命令可以显示所有数据的列表。
执行 “db” 命令可以显示当前数据库对象或集合。
运行”use”命令,可以连接到一个指定的数据库。
集合
集合就是 一组文档,是文档的 “分组容器”,类似关系型数据库的 “表(Table)”,但无需预先定义结构(无 “表结构” 概念)。
集合是动态模式的,也就意味着集合没有固定的结构,在同一集合中,文档的字段数量、字段类型可完全不同(如一个 “用户集合” 中,文档 1 有 “age” 字段,文档 2 可没有)。集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
而且无需手动执行 “创建集合” 命令,首次向不存在的集合插入文档时,MongoDB 会自动创建该集合。
组织集合的惯例是使用.分割不同命名空间的子集合。例如一个具有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors。这是为了使组织结构更清晰,这里的blog集合(这个集合甚至不需要存在)跟它的子集合没有任何关系。
集合的命名规则如下:
- 不能以
system.开头(保留给系统集合,如system.indexes存储索引信息)。 - 不能包含空字符
\0。 - 建议使用小写字母,多个单词用下划线分隔(如
user_logs)。 - 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
特殊集合 capped Collection(固定大小集合):
- 是一种特殊的 “环形缓冲区” 集合,需手动指定固定大小(如 1GB)和最大文档数。
- 当数据达到大小 / 文档数上限时,会自动覆盖最早插入的文档,适合存储 “日志”“监控数据” 等不需要长期保留的时序数据。
文档
文档是MongoDB 的最小数据单元,类似关系型数据库的 “行(Row)”,但以 BSON 格式(二进制 JSON)存储。
文档就是键值对的一个有序集合。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
和关系型数据库一样,文档必须包含 _id
字段,_id 是文档的唯一标识(类似
“主键”),若插入文档时未指定 _id,MongoDB 会自动生成
ObjectId 类型的 _id(12 字节,全局唯一)。
单个文档最大体积为 16MB(避免过大文档导致读写性能下降,大文件需用 GridFS 存储)。
BSON 文档中字段的存储顺序是固定的,且会影响文档的哈希值(与 JSON 字段无序不同)。
示例文档(BSON 格式,可读性展示为 JSON 风格)
1 | { |
文档中的键是字符串,文档中的值可以是多种不同的数据类型,甚至可以是一个完整的内嵌文档。
除少数例外情况,键可以是任意UTF-8字符。文档键命名规范:
- 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
- .和$有特别的意义,只有在特定环境下才能使用。
- 以下划线”_“开头的键是保留的(不是严格要求的)。
MongoDB 的文档不能有重复的键。而且 MongoDB 不但区分类型,而且区分大小写。
1 | {"Foot":"3"} |
文档中的键值对是有序的:
1 | {"x":1,"y":2} |
字段
文档中的 “键值对(Key-Value)”,类似关系型数据库的 “列(Column)”,是数据的最小存储单元。
字段名(Key)必须是字符串,不能包含空字符 \0,不能以
$ 开头(保留给 MongoDB 操作符,如
$set),不能包含
.(避免与嵌套文档路径冲突)。
字段值(Value)支持 BSON 的所有数据类型(如字符串、整数、日期、数组、嵌套文档等),同一字段在不同文档中可对应不同类型(如文档 1 的 “age” 是整数,文档 2 的 “age” 是字符串)。
元数据
数据库的信息是存储在集合中。它们使用了系统的命名空间:dbname.system.*
在MongoDB数据库中名字空间<dbname>.system.*是包含多种系统信息的特殊集合(Collection),如下:
对于修改系统集合中的对象有如下限制。
在{{system.indexes}}插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的drop
index命令将自动更新相关信息)。
{{system.users}}是可修改的。{{system.profile}}是可删
https://blog.csdn.net/qq_42022528/article/details/102794410
物理存储结构
逻辑结构最终会映射到底层磁盘文件,MongoDB
通过一套高效的文件管理机制,将数据、索引、日志等信息存储到磁盘中。物理文件主要位于
MongoDB 的 “数据目录”(启动时通过 --dbpath
指定,默认路径:Windows 为 C:\data\db,Linux 为
/data/db),核心文件类型如下:
核心数据文件:.wt(WiredTiger
存储引擎)
MongoDB 3.2+ 默认使用 WiredTiger
存储引擎(替代早期的 MMAPv1),所有数据(文档、索引)都存储在
.wt 格式的文件中,核心文件包括:
WiredTiger.wt:WiredTiger 引擎的核心元数据文件,存储数据字典、表空间信息等,是引擎启动的关键文件(删除会导致数据丢失)。collection-<UUID>.wt:每个集合对应一个独立的.wt文件(<UUID>是集合的唯一标识),存储该集合的所有文档数据。index-<UUID>-<indexName>.wt:每个索引对应一个独立的.wt文件,存储该索引的键值和指向文档的指针(索引与集合文件分离,便于单独优化)。_mdb_catalog.wt:MongoDB 的 “目录文件”,存储数据库、集合的元数据(如数据库名、集合名、集合与.wt文件的映射关系)。
存储引擎 WiredTiger 是 MongoDB 与磁盘交互的 “中间层”,负责数据的存储、读取、索引维护,WiredTiger 相比早期引擎的核心优势:
- 页式存储:将数据文件划分为固定大小的 “页(Page,默认 4KB)”,读写数据时按 “页” 操作,减少磁盘 IO 次数。
- 缓存机制:维护 “内存缓存(WiredTiger
Cache)”,将热点数据(常用文档、索引)加载到内存,读操作优先命中缓存,大幅提升读性能(默认缓存大小为
“物理内存的 50% - 1GB”,可通过
--wiredTigerCacheSizeGB配置)。 - 压缩算法:默认对数据和索引进行压缩(数据用 Snappy 算法,索引用前缀压缩),可减少磁盘占用(压缩率通常为 30%-50%)。
预写日志(WAL)文件:.wiredtiger_wal
- 作用:WiredTiger 采用 “预写日志” 机制保证数据一致性 ——先将修改操作写入日志文件,再异步刷到数据文件,避免因断电导致未刷盘的数据丢失。
- 文件特点
- 日志文件以
WiredTigerLog.<序号>命名(如WiredTigerLog.0000000001)。 - 单个日志文件默认大小为 100MB,当文件满时自动生成新文件;数据刷盘后,旧日志文件会被自动清理(可通过配置调整保留策略)。
- 日志文件以
诊断日志文件:mongod.log
- 作用:记录 MongoDB 服务的运行日志(启动信息、错误信息、慢查询日志、连接日志等),是排查问题的核心依据。
- 配置:默认输出到控制台,可通过启动参数
--logpath <路径>指定日志文件路径(如--logpath /var/log/mongodb/mongod.log),并通过--logrotate配置日志轮转(避免单个日志文件过大)。
数据分片(Sharding)大规模数据的分布式存储
当单节点存储能力不足时(如数据量达 TB 级),MongoDB 可通过 “分片集群” 将数据分布式存储到多个节点,核心逻辑:
- 分片键(Shard Key):指定一个字段作为 “分片键”(如用户 ID、订单时间),MongoDB 按分片键将集合数据拆分为多个 “数据块(Chunk,默认 64MB)”。
- 分片节点(Shard):每个分片节点存储一部分数据块,所有分片节点组成
“分片集群”,对外提供统一的访问入口(通过路由节点
mongos)。 - 映射关系:分片集群中,每个分片节点的物理文件(
.wt)仅存储该节点负责的数据块,config数据库记录 “分片键 → 数据块 → 分片节点” 的映射关系,确保查询时能快速定位数据所在节点。
而且为避免单节点故障导致数据丢失,MongoDB 可通过 “副本集” 实现数据冗余存储,核心逻辑:
- 节点角色:一个副本集包含 1 个 “主节点(Primary)” 和多个 “从节点(Secondary)”,主节点负责接收所有写操作,从节点通过 “ oplog(操作日志)” 同步主节点的数据。
- 物理文件同步:主节点的写操作会先写入自身的
.wt文件和 WAL 日志,同时记录到local.oplog.rs集合( oplog 存储在local数据库的.wt文件中);从节点定期读取主节点的 oplog,重复执行写操作,确保自身数据与主节点一致。 - 故障切换:当主节点故障时,从节点会通过 “心跳检测” 发现问题,自动选举新的主节点,继续提供服务,整个过程无需人工干预。
MongoDB的数据类型
BSON
BSON 是 Binary JSON(二进制 JSON) 的缩写,是 MongoDB 专门设计的一种二进制数据格式,用于存储文档(Document)和在客户端与服务器之间传输数据,在 MongoDB 中存储文档和进行远程过程调用
它本质是对 JSON 格式的 “二进制优化”—— 保留 JSON 的易读性和灵活结构,同时解决 JSON 性能差、数据类型少的问题,是 MongoDB 实现 “高效存储” 和 “快速序列化 / 反序列化” 的核心基础。
BSON 规范位于 bsonspec.org。
BSON 和 JSON 的核心差异可以这样总结
| 对比维度 | BSON | JSON | 关键影响 |
|---|---|---|---|
| 存储格式 | 二进制 | 纯文本 | BSON 体积小、解析快,适合数据库存储;JSON 易读,适合接口传输。 |
| 数据类型 | 支持 16 种(含 ObjectId、Date、Binary 等) | 仅支持 6 种(字符串、数字、布尔、数组、对象、null) | BSON 无需 “类型伪装”(如 JSON 存日期需转成 “2024-05-01” 字符串)。 |
| 字段顺序 | 字段顺序固定,且会影响文档哈希值 | 字段顺序无意义(不同顺序视为同一对象) | MongoDB 索引依赖 BSON 字段顺序,避免因顺序问题导致索引失效。 |
| 注释支持 | 貌似没有解析器支持注释 | 有解析器支持单行 // 和多行
/* */,但是我们认为JSON不支持注释 |
BSON 专注存储效率,不保留冗余注释信息。 |
| 大小限制 | MongoDB 单文档最大 16MB(避免过大文档影响性能) | 无默认限制,取决于解析器 | 控制单文档体积,保障 MongoDB 读写性能。 |
相比 JSON(文本格式),BSON 针对数据库场景做了关键优化,直接影响 MongoDB 的性能表现,对微服务项目的高并发场景尤为重要:
| 优势维度 | 具体说明 | 对项目的价值 |
|---|---|---|
| 二进制存储,体积更小 | JSON 是纯文本,字符(如引号、逗号)占额外空间;BSON 用二进制编码,字段名仅存储一次(重复字段复用引用),整体体积比 JSON 小 10%-40%。 | 减少磁盘存储占用,降低微服务间(如 API 调用)和 MongoDB 客户端 / 服务器间的网络传输量,提升响应速度。 |
| 支持更多数据类型 | 弥补 JSON 仅支持 “字符串、数字、布尔、数组、对象、null”6 种类型的不足,新增日期、二进制、正则、ObjectId 等关键类型(详见下文)。 | 无需在 Java 代码中手动转换类型(如 JSON 存日期需转字符串,BSON
可直接存 Date 类型),避免类型转换错误。 |
| 更快的序列化 / 反序列化 | 二进制格式无需像 JSON 那样解析文本(如处理转义字符、语法校验),MongoDB 驱动(如 Java Driver)可直接将 BSON 与 Java 对象(POJO)映射,速度比 JSON 快 2-5 倍。 | 减少 ZhiyanPlatform 中 “Java 对象 ↔︎ 数据库数据” 的转换开销,适配高并发读写场景(如订单创建、日志写入)。 |
| 支持有序字段和索引 | BSON 文档中字段的存储顺序是固定的(JSON 字段顺序无意义),且 MongoDB
可基于 BSON 字段直接建立索引(如 _id
索引、复合索引)。 |
保障索引查询的准确性和效率,避免因字段顺序问题导致的查询异常。 |
MongoDB 支持的 16 种 BSON 类型如下,每个 BSON 类型都同时具有整数和字符串标识符,如下表所列:
| 类型 | 数值 | 别名 | 注意 |
|---|---|---|---|
| double | 1 | “double” | |
| 字符串 | 2 | “string” | |
| 对象 | 3 | “object” | |
| 阵列 | 4 | “array” | |
| 二进制数据 | 5 | “binData” | |
| 未定义 | 6 | “undefined” | 已弃用。 |
| ObjectId | 7 | “objectId” | |
| 布尔 | 8 | “bool” | |
| Date | 9 | “date” | |
| null | 10 | “null” | |
| 正则表达式 | 11 | “regex” | |
| 数据库指针 | 12 | “dbPointer” | 已弃用。 |
| JavaScript | 13 | “javascript” | |
| 符号 | 14 | “symbol” | 已弃用。 |
| 带作用域的 JavaScript | 15 | “javascriptWithScope” | 已弃用。 |
| 32 位整数 | 16 | “int” | |
| 时间戳 | 17 | “timestamp” | |
| 64 位整型 | 18 | “long” | |
| Decimal128 | 19 | “decimal” | |
| Min key | -1 | “minKey” | |
| Max key | 127 | “maxKey” |
$type操作符支持使用这些值按 BSON 类型查询字段。$type还支持number别名,它匹配整数、十进制、double 和长整型 BSON 类型。$type聚合操作符返回其参数的 BSON 类型。- 如果
$isNumber聚合操作符的参数是 BSON 整数、十进制、双精度浮点数或长整型,则该操作符会返回true。
最常用的其实也就这六个,结合 Java 类型对应关系更好理解
| BSON 类型 | 类型标识(了解) | 描述 | 对应 Java 类型 | 开发场景示例(ZhiyanPlatform) |
|---|---|---|---|---|
| ObjectId | 0x07 | MongoDB 默认主键类型,12 字节二进制,包含 “时间戳(4B)+ 机器 ID(3B)+ 进程 ID(2B)+ 计数器(3B)”,全局唯一。 | org.bson.types.ObjectId |
作为用户表、订单表的主键(如
userId: ObjectId("60d21b4667d0d8992e610c85"))。 |
| String | 0x02 | UTF-8 编码的字符串,支持正则匹配。 | java.lang.String |
存储用户名、手机号、接口 URL 等(如
username: "zhangsan")。 |
| Int32 | 0x10 | 32 位有符号整数(范围:-2³¹ ~ 2³¹-1)。 | java.lang.Integer |
存储年龄、数量等小数值(如
age: 25、orderCount: 10)。 |
| Int64 | 0x12 | 64 位有符号整数(范围:-2⁶³ ~ 2⁶³-1)。 | java.lang.Long |
存储用户 ID(自定义主键时)、订单金额(分单位,如
amount: 9990)。 |
| Double | 0x01 | 64 位浮点数,支持整数和小数。 | java.lang.Double |
存储价格(如 price: 99.9)、评分(如
score: 4.8)。 |
| Boolean | 0x08 | 布尔值,仅 true 或 false。 |
java.lang.Boolean |
存储状态标识(如
isVip: true、isDeleted: false)。 |
| Date | 0x09 | UTC 时间戳,精确到毫秒(从 1970-01-01 00:00:00 开始计算)。 | java.util.Date /
java.time.LocalDateTime(需配置映射) |
存储创建时间、更新时间(如
createTime: ISODate("2024-05-01T10:00:00Z"))。 |
| Array | 0x04 | 有序的值列表,元素类型可不同(如同时包含字符串、数字)。 | java.util.List(如
List<String>、List<User>) |
存储用户标签(如
tags: ["Java", "微服务"])、订单商品列表(如
products: [{"id": 1, "name": "手机"}, ...])。 |
| Document | 0x03 | 嵌套的 BSON 文档(键值对结构),支持多层嵌套。 | org.bson.Document / 自定义 Java POJO(如
UserAddress) |
存储嵌套信息(如用户地址:address: {"province": "北京", "city": "北京", "detail": "XX街道"})。 |
| Binary Data | 0x05 | 二进制数据,支持存储图片、文件片段等二进制内容。 | byte[] / org.bson.types.Binary |
存储用户头像缩略图(小文件,大文件建议用 MinIO |
二进制数据
在 MongoDB 的 BSON 格式中,binData
是专门用来存储二进制数据的类型(比如图片、加密数据、二进制文件片段等)。它的本质是字节数组,但为了明确
“这些二进制数据该如何解读”,BSON 给 binData 定义了
“子类型”—— 不同子类型对应不同的二进制数据用途,MongoDB
会根据子类型来解析这些字节。
下表显示了子类型:
| 数值 | 说明 |
|---|---|
| 0 | 通用二进制子类型 |
| 1 | 函数数据 |
| 2 | 二进制(旧版) |
| 3 | UUID(旧) |
| 4 | UUID |
| 5 | MD5 |
| 6 | 加密的 BSON 值 |
| 7 | 压缩时间序列数据 5.2 版本中的新增功能。 |
| 8 | 敏感数据,例如密钥或密码。 MongoDB 不会记录子类型为 8
的二进制数据的字面值。相反,MongoDB 会记录占位符值###
。 |
| 9 | 向量数据是由相同类型的数字组成的密集数组。 |
| 128 | 自定义数据 |
子类型是一个数字标识,用来告诉 MongoDB(或开发者):“这段二进制数据属于什么类型,应该用什么逻辑去解释它”。比如子类型为 “5”,就表示这段二进制是 MD5 哈希值;子类型为 “8”,表示是敏感数据(如密码)。
ObjectId
ObjectId 类似唯一主键,可以很快的去生成和排序
ObjectId(对象标识符)很小,可能是唯一的,生成速度快并且是有序的。
ObjectId 值的长度为 12 个字节,包含:
一个 4 字节时间戳,它表示 ObjectId 的创建时间,并以自 UNIX 纪元以来的秒数为单位进行测量。
5 字节的随机值,每个客户端进程生成一次。这个随机值对于机器和进程是唯一的。如果进程重新启动或进程的主节点发生变化,此值会被重新生成。
每个客户端进程的 3字节递增计数器,初始化为随机值。当进程重新启动时,计数器会重置。
对于时间戳和计数器值,最高有效字节在字节序列中最先出现(大端字节序)。对于其他 BSON 值,最低有效字节最先出现(小端字节序)。
如果使用整数值创建对象标识符(ObjectId),则该整数将替换时间戳。
在MongoDB中,存储在标准集合中的每个文档都需要一个唯一的_id字段作为主键。这个键的值可以是任何类型的,默认是个 ObjectId 对象。
如果插入的文档省略了_id 字段,则MongoDB驱动会自动为_id字段生成
ObjectId。这也适用于通过执行 upsert:
true 的更新操作插入的文档。
MongoDB 客户端应添加一个具有唯一 ObjectId 的 _id
字段。为 _id 字段使用 ObjectId 还能带来以下好处:
您可以使用
ObjectId.getTimestamp()方法访问mongosh中的ObjectId创建时间。ObjectID 大致按创建时间排序,但并非完全有序。在包含
ObjectId值的_id字段上对集合排序,大致相当于按创建时间排序。重要
虽然 ObjectId 值会随着时间的推移而增加,但它们不一定是单调的。这是因为它们:
- 仅有一秒的时间分辨率,因此在同一秒内创建的 ObjectId 值不能保证顺序
- 由可能具有不同系统时钟的客户端生成。也就是两个系统时间的机器。
使用 ObjectId()
方法可设置和检索 ObjectId 值。
从 MongoDB 5.0 开始,mongosh
将取代旧版的 mongo shell。ObjectId() 方法在
mongosh 中的运行方式与在旧版的 mongo shell
中的运行方式不同。有关旧版方法的更多信息,请参阅旧版的
mongo Shell。
字符串
在 MongoDB 中,字符串是最基础且常用的数据类型之一,属于 BSON 类型中的
String(类型标识 0x02),采用 UTF-8 编码存储。
通常,在序列化和反序列化 BSON
时,每种编程语言的驱动程序会从该语言的字符串格式转换为
UTF-8。这样就可以在 BSON 字符串中轻松存储大多数国际字符。此外,MongoDB$regex
查询支持在正则表达式字符串中使用 UTF-8。
对于使用 UTF-8 字符集的字符串,使用 sort()
排序通常可以得到正确的结果。但是,由于 sort()
内部使用 C++ strcmp
api,因此某些字符的排序处理可能不正确。 |
|
|---|---|
字符串的常见操作类似 js 的字符串操作
1 | db.users.insertOne({ |
字符串查询操作
精确匹配:
1
db.users.find({ username: "alice" });
模糊匹配(使用正则表达式):
1
2
3
4
5// 查询用户名以 "a" 开头的用户
db.users.find({ username: /^a/ });
// 查询邮箱包含 "example" 的用户
db.users.find({ email: { $regex: "example" } });
字符串排序
1 | // 按用户名升序排序 |
为了提升字符串查询性能,建议为频繁查询的字符串字段建立索引:
单字段索引:
1
db.users.createIndex({ username: 1 });
复合索引(结合其他字段):
1
db.users.createIndex({ username: 1, email: 1 });
文本索引(用于复杂文本搜索):
1
2
3db.articles.createIndex({ content: "text" });
// 全文搜索示例
db.articles.find({ $text: { $search: "MongoDB 教程" } });
日期
“BSON Date”(BSON 日期)是一个 64 位整数,它表示自 UNIX 纪元(1970 年
1 月 1 日)以来的毫秒数(与 Java 中的 java.util.Date
原理一致)。因此,过去与未来的可表示日期范围便可达到约 2.9 亿年。
官方BSON规范 将BSON Date 类型称为 UTC datetime。也就是说,MongoDB 存储的是 UTC 时间(世界标准时间),客户端读取时需自行转换为本地时区(如北京时间需 +8 小时)。
BSON Date 类型为有符号值。[2] 负值代表 1970 年之前的日期。
MongoDB 中与时间相关的核心类型有两种,需明确它们的区别:
| 类型 | 存储形式 | 含义与用途 |
|---|---|---|
Date |
64 位整数(毫秒级 UTC 时间戳) | 存储具体的 “日期时间”(如用户注册时间、订单创建时间),是最常用的时间类型。 |
Timestamp |
64 位整数(seconds + increment) |
用于内部操作(如 oplog 复制日志、分片集群的时间同步),不建议业务直接使用。 |
常见操作示例
插入 Date 类型数据
1 | // 方式1:使用 ISODate 构造函数 |
查询 Date 类型数据
1 | // 查询“注册时间在 2024-10-01 之后”的用户 |
日期运算与格式化
1 | // 提取日期的“年、月、日”(使用聚合管道) |
对 Date 字段建立普通索引,支持范围查询(如
“查询近 30 天数据”):
1 | db.orders.createIndex({ createTime: 1 }); // 1 表示升序 |
日期和字符串的不匹配问题
BSON Date 类型与JavaScript使用Date对象一样,而日期对象和字符串是无法匹配的,所以执行删除、更新和查询操作时类型不当会导致很多问题。
问题的根源就在于
Date类型存储的是 64 位整数(毫秒级 UTC 时间戳),本质是二进制数据。- 字符串是 UTF-8 编码的文本序列,两者在存储结构和类型标识上完全不同。
数据库中是 Date
类型,查询时用字符串
1 | // 错误示例:用字符串匹配日期字段 |
数据库中是字符串(存储日期文本),查询时用 Date
类型
1 | // 错误示例:用 Date 类型匹配字符串字段 |
需要同时支持日期对象和字符串查询(数据迁移场景)
如果存在历史数据用字符串存储日期,新数据用 Date
类型存储的情况,可以通过聚合管道同时处理两种类型:
1 | db.mixed_orders.aggregate([ |
时间戳
BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的 日期 类型不相关。
Timestamp 类型存储的是
(秒数 << 32) + 递增数 的 64
位整数,其中:
- 前32位是一个 time_t 值(与Unix新纪元相差的秒数)
- 后32位是在某秒中操作的一个递增的
序数,同一秒内的递增序列号(防止同一秒内操作的冲突)。
虽然 BSON
格式是小端字节序,因此首先存储最低有效位,但在所有平台上,mongod
实例始终先比较 time_t 值,然后再比较 ordinal
值,不受字节序的影响。
时间戳用的最多的就是 oplog 复杂,副本集同步数据时,用
Timestamp 标记操作的 “逻辑时间”,确保从节点按顺序同步。
在复制过程中,oplog
有一个 ts 字段。该字段的值反映利用 BSON
时间戳值确定的操作时间。
在单个 mongod
实例中,oplog
中的时间戳值总是唯一的。
这种 BSON 时间戳类型供 MongoDB 内部使用。对于多数情况,应用程序开发中需要使用 BSON 日期类型。
插入时需用 Timestamp(seconds, increment)
构造,示例:
1 | db.system.oplog.insertOne({ |
decimal128 BSON
数据类型
decimal128 是 128
位十进制表示形式,用于在四舍五入很重要的情况下存储非常大或非常精确的数字。
它创建于 2009 8 月,作为 IEEE
754-2008浮点修订版的一部分。使用BSON数据类型时如果需要高精度,则应使用
decimal128。
decimal128 支持 34 位十进制数的精度或有效数以及 -6143 到
+6144 的指数范围。有效数字在 decimal128
标准中未进行规范化,允许多种可能的表示形式:10 x 10^-1 = 1 x 10^0 = .1 x 10^1 = .01 x 10^2
等。能力分别以 10^6144 和 10^-6143
的顺序存储最大值和最小值,可以实现很高的精度。
也就是说,这个东西和 Java
BigDecimal 是基本差不多的,这个类也支持的是decimal128
数字。
还记得这个吗
1 | class Main { |
发生这种情况是因为二进制浮点数不能很好地表示基 10
值。上述示例中使用的 0.1 在二进制中的表示为
0.0001100110011001101。decimal128
为了更高的精度由此诞生







