mongodb笔记
安装指南 MongoDB Command Reference MongoDB Manual
性能优化
- 文档扫描量大,没有建立索引
- 查询返回的文档空间过大,使用查询选择踢除不必要的字段
- 限制单次查询的返回数量
- 大数据记录,使用投影选项,只返回必要的字段
- 数据库性能只有在使用所有使用的索引都加载到内存里才是最好的。需要
db.stats()和db.collection.stats输出的信息来帮助计划需要多少磁盘空间和 RAM 才能运行 MongoDB。应该为预期的数据留足够的磁盘空间,包括一些安全空间和热数据(工作数据集合) - 不合理的索引顺序令查询效率收效甚微
索引元数据 `keys = {a:1, b:1, c:1} db.xxx.find({a:xx})` 能利用索引 db.xxx.find({b:xx})` 不能利用索引 - 不要滥用索引,索引本身是有成本的。大量增删改记录,会刷新索引信息。不要让索引本身对业务查询造成性问题
- 尝试给经常用在查询条件的字段建立索引,加速查询
- 自托管 MongoDB 设置 ulimit 规定 ulimit 设置参考
索引
-
默认主键有索引作用,ObjectId的生成算法,具备索引特性。 | 字节 | 含义 | |—–|——| | 4B | 时间戳(秒级,文档创建时间) | | 5B | 机器+进程随机数(保证不同机器/进程唯一性) | | 3B | 自增计数器(同一秒内的序号) |
- 复合主键不等于复合索引
- 复合主键仍然以
_id的对象整体计算得的值,来主键索引记录。查询时不能缺少复合主键的任何一个字段 - 而复合索引跟多个索引字段,分批索引。查询时可能缺少其它字段
- 复合主键仍然以
- 建立索引
db.runCommand({ createIndexes: "<collection-name>", indexes: [ { key: { <field1>: <type>, <field2>: <type>, ... }, name: "<index-name>", // 其他选项... }, // 可以添加多个索引 ] })
| 参数 | 说明 |
|---|---|
| createIndexes | 集合名称(字符串),表示要在哪个集合上创建索引 |
| indexes | 索引数组,每个对象定义一个索引 |
| key | 索引的字段及其排序方式(1 表示升序,-1 表示降序) |
| name | 索引名称(可选,不填则自动生成) |
| 其他选项 | 如 unique、sparse、expireAfterSeconds 等 |
| 选项 | 说明 |
|---|---|
| unique: true | 创建唯一索引,禁止重复值 |
| sparse: true | 稀疏索引,只包含有该字段的文档 |
| expireAfterSeconds: N | TTL 索引,用于自动删除过期文档(N 是秒数) |
| background: true | 后台创建索引(避免阻塞其他操作) |
| partialFilterExpression | 创建部分索引(只对满足条件的文档建立索引) |
副本集
uri连接规则
- uri尽管填的是从节点地址x.x.x.x:27017,实际上mongodb client,在从节点返回的副本集集群信息中,选择一个主节点连接
mongodb://admin:123456@x.x.x.x:27017/?authSource=admin如果需要强制连接从节点,需要增加参数
&readPreference=secondary&directConnection=true:mongodb://admin:admin123@10.9.0.12:27017/?authSource=admin&readPreference=secondary&directConnection=true mongodb://yunying:R7mK9pQx2NvB8wE@192.168.2.152:27017/?authSource=admin&readPreference=secondary&directConnection=true
创建副本集
- 各节点mongod.conf增加集群名称 ``` replication: replSetName: “rs0” # 副本集名称,所有节点必须一致
security: authorization: enabled # 生产环境必须启用认证
- 连接任一节点
mongosh --host 192.168.1.101 --port 27017
// 初始化副本集 rs.initiate({ _id: “rs0”, members: [ { _id: 0, host: “192.168.1.101:27017”, priority: 2 }, { _id: 1, host: “192.168.1.102:27017”, priority: 1 }, { _id: 2, host: “192.168.1.103:27017”, priority: 1 } ] })
// 查看副本集状态
rs.status()
## 重新配置副本集
// 1. 获取当前配置(即使显示旧IP也没关系)
cfg = rs.conf()
// 2. 修改每个成员的 host 为新IP(保持 _id 不变!)
cfg.members[0].host = "192.168.3.66:27017" // node1 新IP
cfg.members[1].host = "192.168.3.201:27017" // node2 新IP
cfg.members[2].host = "192.168.3.93:27017" // node3 新IP
// 3. 可选:调整优先级(确保某节点优先成为 Primary)
cfg.members[0].priority = 2
cfg.members[1].priority = 1
cfg.members[2].priority = 1
// 4. 强制重新配置(因多数节点可能不可达,需加 force)
rs.reconfig(cfg, { force: true })
# 设置用户权限
### 设置只读账号
```javascript
use admin
db.createUser({
user: "yunying",
pwd: "R7mK9pQx2NvB8wE",
roles: [
{ role: "read", db: "xxxxxx" } // 只读权限,仅此库
]
})
修改密码
db.changeUserPassword("yunying", "R7mK9pQx2NvB8wE")
备份与恢复
方法一:mongodump & mongorestore
导出所有数据实例,默认对当前运行的 db 实例导出、导入的对象操作。
# 导出
mongodump -h <ADDR> -d <DB_NAME> -o $EXPORT_PATH --gzip
# 导入
mongorestore -h <ADDR> -d <DB_NAME> $IMPORT_PATH --gzip
方法二:mongoexport & mongoimport
mongoexport
mongoimport
两种方式的区别
注意:避免在生产环境中使用 mongoimport 和 mongoexport 进行完整实例备份。它们不能可靠地保留所有 BSON 数据类型,因为 JSON 只能表示 BSON 支持的类型的一个子集。对于此类功能,请使用 MongoDB 备份方法中描述的 mongodump 和 mongorestore。
- mongodump 导出的是 BSON 格式,是二进制形式,可以使用 mongo 自带的 bsondump 命令查看里面的数据
- mongoexport 导出的则是文本,可以是 CSV、JSON 格式
- JSON 可读性强但体积较大,BSON 是二进制文件,体积小但对人类几乎没有可读性
- 在一些 MongoDB 版本之间,BSON 格式可能会随版本不同而有所不同,所以不同版本之间用 mongodump/mongorestore 可能不会成功,具体要看版本之间的兼容性
- 当无法使用 BSON 进行跨版本的数据迁移的时候,使用 JSON 格式即 mongoexport/mongoimport 是一个可选项
- 跨版本的 mongodump/mongorestore 个人并不推荐,实在要做请先检查文档看两个版本是否兼容
- JSON 虽然具有较好的跨版本通用性,但其只保留了数据部分,不保留索引、账户等其他基础信息。使用时应该注意。
数据库部署&配置
日志轮转
/var/log/mongodb/mongod.log 是 MongoDB 的日志文件,它记录了 MongoDB 数据库服务器运行过程中的各种信息。这个文件在排查问题、优化性能、监控数据库状态等方面非常有用。
- 如果没有配置 logrotate 或 MongoDB 未启用日志轮转,日志会一直增长,直到占满磁盘
- 如果开启了
logLevel: 1或更高(调试级别),会记录大量细节。例如使用了setLogLevel()设置了详细日志
配置步骤
-
编辑 MongoDB 的配置文件(通常是
/etc/mongod.conf),确保包含以下内容:systemLog: destination: file path: /var/log/mongodb/mongod.log logAppend: true logRotate: reopen -
设置 logrotate 规则:
sudo nano /etc/logrotate.d/mongodb -
添加以下内容:
/var/log/mongodb/mongod.log { daily missingok rotate 7 compress notifempty create 0644 mongod mongod postrotate systemctl kill -s USR1 mongod endscript }
基本操作
连接数据库
// 连接指定数据库
mongo YOUR_TARGET_DB
// 显示当前连接的数据库
db
// 显示所有数据库
show dbs
// 连接/创建数据库
use YOUR_TARGET_DB
// 删除当前数据库
db.dropDatabase()
// 显示所有集合
show collections
嵌套文档查询
// 使用点号 `.` 访问嵌套字段:
db.YOUR_TARGET_COLLECTION.find({ "FIELD1.FIELD2": "XXXXX" })
//返回第一个匹配的文档
db.YOUR_TARGET_COLLECTION.findOne({ field: value });
// 统计集合中文档总数
db.YOUR_TARGET_COLLECTION.countDocuments()
// 或者使用 count()(已过时,不推荐)
db.YOUR_TARGET_COLLECTION.count()
// 带条件的计数
db.YOUR_TARGET_COLLECTION.countDocuments({ status: "active" })
逻辑表达式
$and
第一个结果为 false 不再执行第二个结果:
{
$and: [
{ startTime: { $lte: 1552713970.0 } },
{ endTime: { $gte: 1552713970.0 } }
]
}
$or
{
$or: [
{ status: "A" },
{ qty: { $lt: 30 } }
]
}
比较操作符
// 等于
db.YOUR_TARGET_COLLECTION.find({ field: value });
// 大于
// field > value
db.YOUR_TARGET_COLLECTION.find({ field: { $gt: value } });
// 小于
// field < value
db.YOUR_TARGET_COLLECTION.find({ field: { $lt: value } });
// 大于等于
// field >= value
db.YOUR_TARGET_COLLECTION.find({ field: { $gte: value } });
// 小于等于
// field <= value
db.YOUR_TARGET_COLLECTION.find({ field: { $lte: value } });
// 不等于
// field != value
db.YOUR_TARGET_COLLECTION.find({ field: { $ne: value } });
// 范围查询
// value1 < field < value2
db.YOUR_TARGET_COLLECTION.find({ field: { $gt: value1, $lt: value2 } });
数组查询
// 匹配数组中的任意一个值
// field IN [v1, v2, v3]
db.YOUR_TARGET_COLLECTION.find({ field: { $in: [v1, v2, v3] } });
// 不匹配数组中的任何值
// field NOT IN [v1, v2, v3]
db.YOUR_TARGET_COLLECTION.find({ field: { $nin: [v1, v2, v3] } });
// 同时包含多个值
// field 包含 v1, v2, v3
db.YOUR_TARGET_COLLECTION.find({ field: { $all: [v1, v2, v3] } });
// 数组长度
// field 数组长度为 3
db.YOUR_TARGET_COLLECTION.find({ field: { $size: 3 } });
// 示例
// 匹配 {name: 'David', age: 26, favorite_number: [6, 7, 9]}
db.users.find({ favorite_number: { $size: 3 } });
取模查询
// field % v1 == v2
db.YOUR_TARGET_COLLECTION.find({ field: { $mod: [v1, v2] } });
- 取模不能利用索引,需要搭配其它索引字段配合使用
更新文档
update 方法
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
参数说明:
query: 更新条件,类似于 SQL 的 WHERE 子句update: 更新操作符和值,类似于 SQL 的 SET 子句upsert: 可选,如果不存在 update 的记录,是否插入新文档,默认为 falsemulti: 可选,是否更新多条记录,默认为 false(只更新第一条)writeConcern: 可选,抛出异常的级别
更新操作符
$set - 更新或添加字段
注意:如果不使用
$操作符,<update>参数会完全替换匹配的文档,而不是更新特定字段。
// 更新或添加字段
db.collection.update(
{ FIELD: VALUE },
{ $set: { field1: "new value" } },
{ multi: true }
);
$unset - 删除字段
// 删除字段
db.collection.update(
{ FIELD: VALUE },
{ $unset: { fieldToRemove: 1 } }
);
$inc - 自增/自减
// 自增
db.collection.update(
{ FIELD: VALUE },
{ $inc: { counter: 1 } } // 增加1
);
// 自减
db.collection.update(
{ FIELD: VALUE },
{ $inc: { counter: -1 } } // 减少1
);
// 原子操作示例
db.tb_system_param.findAndModify({
query: { "id": 1 },
update: { $inc: { "value": 1 } },
upsert: true
});
替换文档
// 替换整个文档(不推荐,会覆盖整个文档)
db.collection.save({
_id: ObjectId("..."), // 必须包含 _id
newField1: "value1",
newField2: "value2"
});
删除文档和集合
删除文档
// 删除匹配条件的所有文档
db.YOUR_TARGET_COLLECTION.remove({ field: value });
// 只删除一条匹配的文档
db.YOUR_TARGET_COLLECTION.deleteOne({ field: value });
// 删除所有匹配的文档
db.YOUR_TARGET_COLLECTION.deleteMany({ field: value });
删除集合
// 删除整个集合(包括所有文档)
db.YOUR_TARGET_COLLECTION.drop();
原文:
https://lizijie.github.io/2025/12/21/mongodb%E7%AC%94%E8%AE%B0.html
作者github:
https://github.com/lizijie
</b>