08-27-周三_17-09-29
This commit is contained in:
3
数据库/MongoDB_2025/MongoDB.md
Normal file
3
数据库/MongoDB_2025/MongoDB.md
Normal file
@@ -0,0 +1,3 @@
|
||||
[MongoDB官网地址](https://www.mongodb.com/)
|
||||
|
||||
[MongoDB项目地址](https://github.com/mongodb/mongo)
|
||||
210
数据库/MongoDB_2025/MongoDB分片.md
Normal file
210
数据库/MongoDB_2025/MongoDB分片.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# 分片(Sharding)
|
||||
|
||||
当数据量增长到单个副本集无法承载,或者写操作的吞吐量达到单台主节点的极限时,就需要通过分片(Sharding)来进行水平扩展。本章节将深入探讨 MongoDB 分片集群的架构、核心组件、分片键的选择策略以及如何部署和管理一个分片集群。
|
||||
|
||||
---
|
||||
|
||||
## 分片概述
|
||||
|
||||
### 什么是分片?
|
||||
|
||||
分片是一种将大型数据集水平分区到多个服务器(或分片)上的数据库架构模式。每个分片都是一个独立的副本集,存储着整个数据集的一部分。通过分片,MongoDB 可以将读写负载分布到多个服务器上,从而实现近乎无限的水平扩展能力。
|
||||
|
||||
### 为什么需要分片?
|
||||
|
||||
1. **存储容量扩展**: 当数据量超过单台服务器的磁盘容量时,可以通过增加分片来扩展存储空间。
|
||||
2. **读写吞吐量提升**: 通过将负载分布到多个分片,可以显著提高整个集群的读写处理能力。
|
||||
3. **高可用性**: 分片集群的每个分片本身就是一个副本集,因此继承了副本集的高可用性特性。
|
||||
|
||||
---
|
||||
|
||||
## 分片集群架构
|
||||
|
||||
一个 MongoDB 分片集群由以下三个核心组件构成:
|
||||
|
||||
1. **分片 (Shard)**
|
||||
- **作用**: 存储数据的单元。每个分片都是一个独立的 MongoDB 副本集,以保证其高可用性。
|
||||
- **职责**: 存储集合数据的一个子集(Chunk)。
|
||||
|
||||
2. **查询路由 (Query Router / `mongos`)**
|
||||
- **作用**: 客户端的入口。`mongos` 是一个轻量级的无状态进程,它接收客户端的请求,并将其路由到正确的分片上。
|
||||
- **职责**: 从配置服务器获取元数据,根据分片键将查询路由到目标分片,并聚合来自多个分片的结果返回给客户端。
|
||||
|
||||
3. **配置服务器 (Config Server)**
|
||||
- **作用**: 存储集群的元数据。这些元数据包含了数据在各个分片上的分布情况(哪个 Chunk 在哪个 Shard)。
|
||||
- **职责**: 管理集群的配置信息。从 MongoDB 3.4 开始,配置服务器必须部署为副本集(CSRS),以保证其高可用性。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 分片键(Shard Key)
|
||||
|
||||
分片键是决定数据如何在各个分片之间分布的关键。选择一个好的分片键至关重要,它直接影响到分片集群的性能和效率。
|
||||
|
||||
### 分片键的选择策略
|
||||
|
||||
一个理想的分片键应该具备以下特征:
|
||||
|
||||
- **高基数 (High Cardinality)**: 分片键应该有大量可能的值,以便将数据均匀地分布到多个 Chunk 中。
|
||||
- **低频率 (Low Frequency)**: 分片键的值应该被均匀地访问,避免出现热点数据(Hot Spot)。
|
||||
- **非单调变化 (Non-Monotonic)**: 分片键的值不应随时间单调递增或递减,这会导致所有的写操作都集中在最后一个分片上。
|
||||
|
||||
### 分片策略
|
||||
|
||||
1. **范围分片 (Ranged Sharding)**
|
||||
- **描述**: 根据分片键的范围将数据分成不同的块(Chunk)。
|
||||
- **优点**: 对于基于范围的查询(如 `find({ x: { $gt: 10, $lt: 20 } })`)非常高效,因为 `mongos` 可以直接将查询路由到存储该范围数据的分片。
|
||||
- **缺点**: 如果分片键是单调变化的(如时间戳),容易导致写操作集中在单个分片上。
|
||||
|
||||
2. **哈希分片 (Hashed Sharding)**
|
||||
- **描述**: 计算分片键的哈希值,并根据哈希值的范围来分片。
|
||||
- **优点**: 能够将数据在各个分片之间均匀分布,保证了写操作的负载均衡。
|
||||
- **缺点**: 对于范围查询不友好,因为相邻的分片键值可能被哈希到不同的分片上,导致查询需要广播到所有分片。
|
||||
|
||||
3. **标签感知分片 (Tag Aware Sharding)**
|
||||
- **描述**: 允许管理员通过标签(Tag)将特定范围的数据块(Chunk)分配到特定的分片上。例如,可以将美国用户的数据放在位于美国的服务器上,以降低延迟。
|
||||
|
||||
---
|
||||
|
||||
## Chunks 和 Balancer
|
||||
|
||||
### 数据块 (Chunk)
|
||||
|
||||
- Chunk 是分片集合中一段连续的数据范围(基于分片键)。MongoDB 会试图保持 Chunk 的大小在一个可配置的范围内(默认为 64MB)。
|
||||
- 当一个 Chunk 的大小超过配置值时,它会分裂成两个更小的 Chunk。
|
||||
|
||||
### 均衡器 (Balancer)
|
||||
|
||||
- Balancer 是一个后台进程,它负责在各个分片之间迁移 Chunk,以确保数据在整个集群中均匀分布。
|
||||
- 当某个分片的 Chunk 数量远多于其他分片时,Balancer 会自动启动,并将一些 Chunk 从最拥挤的分片迁移到最空闲的分片。
|
||||
- 均衡过程会消耗 I/O 和网络资源,可以在业务高峰期临时禁用 Balancer。
|
||||
|
||||
---
|
||||
|
||||
## 实践操作
|
||||
|
||||
### 需求描述
|
||||
|
||||
构建一个完整的 MongoDB 分片集群,模拟电商平台的订单数据存储场景。该场景需要处理大量的订单数据,要求系统具备高可用性和水平扩展能力。通过实际操作来理解分片集群的部署、配置和管理过程。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```shell
|
||||
# 1. 创建数据目录
|
||||
mkdir -p /data/mongodb-sharding/{config1,config2,config3,shard1,shard2,mongos}
|
||||
|
||||
# 2. 启动配置服务器副本集 (CSRS)
|
||||
# 启动三个配置服务器实例
|
||||
mongod --configsvr --replSet configReplSet --port 27019 --dbpath /data/mongodb-sharding/config1 --fork --logpath /data/mongodb-sharding/config1.log
|
||||
mongod --configsvr --replSet configReplSet --port 27020 --dbpath /data/mongodb-sharding/config2 --fork --logpath /data/mongodb-sharding/config2.log
|
||||
mongod --configsvr --replSet configReplSet --port 27021 --dbpath /data/mongodb-sharding/config3 --fork --logpath /data/mongodb-sharding/config3.log
|
||||
|
||||
# 连接到配置服务器并初始化副本集
|
||||
mongosh --port 27019
|
||||
# 在 mongosh shell 中执行:
|
||||
rs.initiate({
|
||||
_id: "configReplSet",
|
||||
configsvr: true,
|
||||
members: [
|
||||
{ _id: 0, host: "localhost:27019" },
|
||||
{ _id: 1, host: "localhost:27020" },
|
||||
{ _id: 2, host: "localhost:27021" }
|
||||
]
|
||||
})
|
||||
|
||||
# 验证配置服务器状态
|
||||
rs.status()
|
||||
# 预期结果:显示三个配置服务器节点,其中一个为 PRIMARY,两个为 SECONDARY
|
||||
|
||||
# 3. 启动分片副本集
|
||||
# 启动第一个分片
|
||||
mongod --shardsvr --replSet shard1ReplSet --port 27022 --dbpath /data/mongodb-sharding/shard1 --fork --logpath /data/mongodb-sharding/shard1.log
|
||||
|
||||
# 启动第二个分片
|
||||
mongod --shardsvr --replSet shard2ReplSet --port 27023 --dbpath /data/mongodb-sharding/shard2 --fork --logpath /data/mongodb-sharding/shard2.log
|
||||
|
||||
# 初始化分片副本集
|
||||
mongosh --port 27022
|
||||
# 在 mongosh shell 中执行:
|
||||
rs.initiate({
|
||||
_id: "shard1ReplSet",
|
||||
members: [{ _id: 0, host: "localhost:27022" }]
|
||||
})
|
||||
|
||||
mongosh --port 27023
|
||||
# 在 mongosh shell 中执行:
|
||||
rs.initiate({
|
||||
_id: "shard2ReplSet",
|
||||
members: [{ _id: 0, host: "localhost:27023" }]
|
||||
})
|
||||
|
||||
# 4. 启动 mongos 查询路由
|
||||
mongos --configdb configReplSet/localhost:27019,localhost:27020,localhost:27021 --port 27017 --fork --logpath /data/mongodb-sharding/mongos.log
|
||||
|
||||
# 5. 连接到 mongos 并添加分片
|
||||
mongosh --port 27017
|
||||
# 在 mongosh shell 中执行:
|
||||
sh.addShard("shard1ReplSet/localhost:27022")
|
||||
sh.addShard("shard2ReplSet/localhost:27023")
|
||||
|
||||
# 验证分片状态
|
||||
sh.status()
|
||||
# 预期结果:显示两个分片已成功添加到集群中
|
||||
|
||||
# 6. 为数据库和集合启用分片
|
||||
# 启用数据库分片
|
||||
sh.enableSharding("ecommerce")
|
||||
|
||||
# 为订单集合创建分片键并启用分片
|
||||
sh.shardCollection("ecommerce.orders", { "customerId": 1 })
|
||||
# 为订单集合创建分片键:哈希策略
|
||||
# sh.shardCollection("ecommerce.orders", {"customerId": "hashed"})
|
||||
|
||||
# 设置新的 chunk 大小(单位:MB)
|
||||
db.settings.updateOne(
|
||||
{ _id: "chunksize" },
|
||||
{ $set: { value: 1 } },
|
||||
{ upsert: true }
|
||||
)
|
||||
|
||||
# 7. 插入测试数据
|
||||
use ecommerce
|
||||
for (let i = 1; i <= 100000; i++) {
|
||||
db.orders.insertOne({
|
||||
customerId: Math.floor(Math.random() * 1000) + 1,
|
||||
orderDate: new Date(2024, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1),
|
||||
amount: Math.random() * 1000,
|
||||
products: ["product" + (Math.floor(Math.random() * 100) + 1)]
|
||||
})
|
||||
}
|
||||
|
||||
# 8. 等待3min后,观察数据分布和均衡过程
|
||||
## partitioned: false 新版本已默认开启,可以忽略
|
||||
sh.status()
|
||||
# 预期结果:显示数据已分布到不同的分片上,可以看到 chunks 的分布情况
|
||||
|
||||
# 查看集合的分片信息
|
||||
db.orders.getShardDistribution()
|
||||
# 预期结果:显示每个分片上的文档数量和数据大小
|
||||
sh.getShardedDataDistribution()
|
||||
# 预期结果:显示每个分片上的数据库和集合的分布情况
|
||||
|
||||
# 9. 测试分片键查询性能
|
||||
db.orders.find({customerId: 123}).explain("executionStats")
|
||||
# 预期结果:查询只会路由到包含该 customerId 数据的特定分片
|
||||
|
||||
# 加速迁移
|
||||
use config
|
||||
db.settings.update(
|
||||
{ "_id": "balancer" },
|
||||
{ $set:
|
||||
{
|
||||
"_waitForDelete": false,
|
||||
"_secondaryThrottle": false,
|
||||
"writeConcern": { "w": "1" }
|
||||
}
|
||||
},
|
||||
{ upsert: true }
|
||||
)
|
||||
```
|
||||
188
数据库/MongoDB_2025/MongoDB副本集.md
Normal file
188
数据库/MongoDB_2025/MongoDB副本集.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# 副本集(Replica Set)
|
||||
|
||||
为了提供高可用性和数据冗余,MongoDB 使用副本集(Replica Set)的机制。本章节将详细介绍副本集的概念、架构、工作原理以及如何配置和管理一个副本集,确保数据库服务能够抵御单点故障。
|
||||
|
||||
---
|
||||
|
||||
## 副本集概述
|
||||
|
||||
### 什么是副本集?
|
||||
|
||||
副本集是一组维护相同数据集的 `mongod` 进程。它由一个**主节点 (Primary)** 和多个**从节点 (Secondary)** 组成,共同保证了数据的冗余和高可用性。
|
||||
|
||||
- **主节点 (Primary)**: 接收所有的写操作。一个副本集在任何时候最多只能有一个主节点。
|
||||
- **从节点 (Secondary)**: 从主节点异步复制数据。从节点可以接受读操作,从而分担主节点的读负载。
|
||||
|
||||
### 副本集的目标
|
||||
|
||||
1. **数据冗余 (Data Redundancy)**: 数据在多个服务器上有副本,防止因单台服务器硬件故障导致的数据丢失。
|
||||
2. **高可用性 (High Availability)**: 当主节点发生故障时,副本集会自动进行故障转移(Failover),从剩下的从节点中选举出一个新的主节点,从而保证服务的持续可用。
|
||||
3. **读写分离 (Read Scaling)**: 客户端可以将读请求路由到从节点,从而分散读负载,提高读取性能。
|
||||
|
||||
---
|
||||
|
||||
## 副本集架构与工作原理
|
||||
|
||||
### 副本集成员
|
||||
|
||||
一个典型的副本集包含以下成员:
|
||||
|
||||
- **主节点 (Primary)**: 唯一的写操作入口。
|
||||
- **从节点 (Secondary)**: 复制主节点的数据,可以处理读请求。从节点也可以被配置为:
|
||||
- **优先级为 0 的成员 (Priority 0 Member)**: 不能被选举为主节点,适合用于备份或离线分析。
|
||||
- **隐藏成员 (Hidden Member)**: 对客户端不可见,不能处理读请求,通常用于备份。
|
||||
- **延迟成员 (Delayed Member)**: 数据会比主节点延迟一段时间,可用于恢复误操作的数据。
|
||||
- **仲裁者 (Arbiter)**: 只参与选举投票,不存储数据副本。它的存在是为了在成员数量为偶数时,打破平局,确保能够选举出主节点。
|
||||
|
||||
### 数据同步(复制)
|
||||
|
||||
- 主节点记录其所有的操作到一个特殊的 capped collection 中,称为 **oplog (operations log)**。
|
||||
- 从节点持续地从主节点的 oplog 中拉取新的操作,并在自己的数据集上应用这些操作,从而保持与主节点的数据同步。
|
||||
- 这个过程是异步的,因此从节点的数据可能会有轻微的延迟。
|
||||
|
||||
### 选举过程(Failover)
|
||||
|
||||
当主节点在一定时间内(默认为 10 秒)无法与副本集中的其他成员通信时,会触发一次选举。
|
||||
|
||||
1. **触发选举**: 从节点发现主节点不可达。
|
||||
2. **选举投票**: 剩下的健康成员会进行投票,选举一个新的主节点。投票的依据包括成员的优先级、oplog 的新旧程度等。
|
||||
3. **产生新的主节点**: 获得大多数票数(`(N/2) + 1`,其中 N 是副本集总成员数)的从节点会成为新的主节点。
|
||||
4. **恢复同步**: 旧的主节点恢复后,会作为从节点重新加入副本集。
|
||||
|
||||
---
|
||||
|
||||
## 读写关注点(Read and Write Concern)
|
||||
|
||||
### 写关注点 (Write Concern)
|
||||
|
||||
写关注点决定了写操作在向客户端确认成功之前,需要被多少个副本集成员确认。
|
||||
|
||||
- **`w: 1` (默认)**: 写操作只需在主节点成功即可返回。
|
||||
- **`w: "majority"`**: 写操作需要被大多数(`(N/2) + 1`)数据承载成员确认后才返回。这是推荐的设置,可以防止在故障转移期间发生数据回滚。
|
||||
- **`j: true`**: 要求写操作在返回前写入到磁盘日志(journal)。
|
||||
|
||||
### 读偏好 (Read Preference)
|
||||
|
||||
读偏好决定了客户端从哪个成员读取数据。
|
||||
|
||||
- **`primary` (默认)**: 只从主节点读取,保证数据最新。
|
||||
- **`primaryPreferred`**: 优先从主节点读取,如果主节点不可用,则从从节点读取。
|
||||
- **`secondary`**: 只从从节点读取,可以分担主节点负载,但可能读到稍有延迟的数据。
|
||||
- **`secondaryPreferred`**: 优先从从节点读取,如果没有可用的从节点,则从主节点读取。
|
||||
- **`nearest`**: 从网络延迟最低的成员读取,不关心是主节点还是从节点。
|
||||
|
||||
---
|
||||
|
||||
## 实践操作
|
||||
|
||||
### 需求描述
|
||||
|
||||
某电商公司的 MongoDB 数据库需要实现高可用性架构,要求:
|
||||
1. 数据库服务 24/7 不间断运行
|
||||
2. 单节点故障时自动故障转移
|
||||
3. 支持读写分离以提升性能
|
||||
4. 数据冗余备份防止数据丢失
|
||||
|
||||
我们需要搭建一个三成员副本集来满足这些需求,并验证其高可用性和读写分离功能。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```shell
|
||||
# 1. 创建数据目录
|
||||
mkdir -p /data/rs{1,2,3}
|
||||
|
||||
# 2. 启动三个 mongod 实例
|
||||
# 实例 1 (Primary 候选)
|
||||
mongod --port 27027 --dbpath /data/rs1 --replSet myReplicaSet --fork --logpath /var/log/mongodb/rs1.log
|
||||
|
||||
# 实例 2 (Secondary 候选)
|
||||
mongod --port 27028 --dbpath /data/rs2 --replSet myReplicaSet --fork --logpath /var/log/mongodb/rs2.log
|
||||
|
||||
# 实例 3 (Secondary 候选)
|
||||
mongod --port 27029 --dbpath /data/rs3 --replSet myReplicaSet --fork --logpath /var/log/mongodb/rs3.log
|
||||
|
||||
# 3. 连接到第一个实例并初始化副本集
|
||||
mongosh --port 27027
|
||||
|
||||
# 在 mongo shell 中执行以下命令
|
||||
# 初始化副本集配置
|
||||
config = {
|
||||
_id: "myReplicaSet",
|
||||
members: [
|
||||
{ _id: 0, host: "localhost:27027", priority: 2 },
|
||||
{ _id: 1, host: "localhost:27028", priority: 1 },
|
||||
{ _id: 2, host: "localhost:27029", priority: 1 }
|
||||
]
|
||||
}
|
||||
|
||||
# 执行初始化
|
||||
rs.initiate(config)
|
||||
|
||||
# 等待几秒后验证副本集状态
|
||||
rs.status()
|
||||
|
||||
# 预期结果:显示一个 PRIMARY 节点和两个 SECONDARY 节点
|
||||
|
||||
# 4. 测试写操作(在主节点执行)
|
||||
use testDB
|
||||
db.products.insertOne({name: "iPhone 14", price: 999, stock: 100})
|
||||
db.products.insertOne({name: "MacBook Pro", price: 2499, stock: 50})
|
||||
|
||||
# 验证写入成功
|
||||
db.products.find()
|
||||
# 预期结果:显示刚插入的两条记录
|
||||
|
||||
# 5. 测试读写分离
|
||||
# 连接到副本集不设置读偏好,默认从主节点读取
|
||||
mongosh "mongodb://localhost:27027,localhost:27028,localhost:27029/?replicaSet=myReplicaSet"
|
||||
# 数据来源一直为 27027
|
||||
use testDB
|
||||
myReplicaSet [primary] test> db.products.find().explain().serverInfo
|
||||
|
||||
# 连接到副本集并设置读偏好为从节点
|
||||
mongosh "mongodb://localhost:27027,localhost:27028,localhost:27029/?replicaSet=myReplicaSet&readPreference=secondary"
|
||||
# readPreference 仅控制查询路由,不改变连接目标,但数据来源为 27028/27029
|
||||
use testDB
|
||||
db.products.find().explain().serverInfo
|
||||
|
||||
# 6. 模拟故障转移测试
|
||||
# 在另一个终端中找到主节点进程并停止
|
||||
ps aux | grep "mongod.*27027"
|
||||
kill <主节点进程ID>
|
||||
|
||||
# 回到 mongo shell 观察选举过程
|
||||
rs.status()
|
||||
# 等待 10-30 秒后再次检查
|
||||
rs.status()
|
||||
|
||||
# 预期结果:原来的一个从节点变成新的主节点
|
||||
# 例如 localhost:27028 变成 PRIMARY 状态
|
||||
|
||||
# 7. 验证故障转移后的读写功能
|
||||
# 在新主节点上执行写操作
|
||||
mongosh --port 27028
|
||||
use testDB
|
||||
db.products.insertOne({name: "iPad Air", price: 599, stock: 75})
|
||||
|
||||
# 验证数据写入成功
|
||||
db.products.find()
|
||||
# 预期结果:显示包括新插入记录在内的所有数据
|
||||
|
||||
# 8. 恢复故障节点
|
||||
# 重新启动之前停止的节点
|
||||
mongod --port 27027 --dbpath /data/rs1 --replSet myReplicaSet --fork --logpath /var/log/mongodb/rs1.log
|
||||
|
||||
# 验证节点重新加入副本集
|
||||
rs.status()
|
||||
# 预期结果:localhost:27027 重新出现在成员列表中,状态为 SECONDARY,因为 27027 节点优先级配置更高,会主动出发选举并抢占主节点角色,这是MongoDB副本集设计的正常行为
|
||||
|
||||
# 9. 验证数据同步
|
||||
# 连接到恢复的节点
|
||||
mongosh --port 27027
|
||||
use testDB
|
||||
db.getMongo().setReadPref("secondary")
|
||||
db.products.find()
|
||||
# 预期结果:显示所有数据,包括故障期间插入的记录,证明数据同步正常
|
||||
|
||||
# [扩展] 10. 性能测试 - 验证读写分离效果
|
||||
```
|
||||
161
数据库/MongoDB_2025/MongoDB基础操作.md
Normal file
161
数据库/MongoDB_2025/MongoDB基础操作.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# 基础操作
|
||||
|
||||
本章节将引导大家学习 MongoDB 的基础操作,包括如何使用 MongoDB Shell、管理数据库和集合,以及对文档进行核心的增删改查(CRUD)操作。掌握这些基础是进行更高级应用的前提。
|
||||
|
||||
---
|
||||
|
||||
## MongoDB Shell 使用
|
||||
|
||||
MongoDB Shell 是一个功能强大的交互式 JavaScript 接口,用于管理和操作 MongoDB 数据库。
|
||||
|
||||
- **连接数据库**:
|
||||
打开终端,输入 `mongo` 或 `mongosh` 命令即可连接到本地默认的 MongoDB 实例 (mongodb://127.0.0.1:27017)。
|
||||
```shell
|
||||
mongosh "mongodb://<host>:<port>/<database>" -u <username> -p
|
||||
```
|
||||
|
||||
- **基本命令**:
|
||||
- `show dbs`: 显示所有数据库列表。
|
||||
- `use <db_name>`: 切换到指定数据库,如果不存在则在首次插入数据时创建。
|
||||
- `show collections`: 显示当前数据库中的所有集合。
|
||||
- `db`: 显示当前所在的数据库。
|
||||
- `db.stats()`: 显示当前数据库的状态信息。
|
||||
- `exit` 或 `quit()`: 退出 Shell。
|
||||
|
||||
---
|
||||
|
||||
## 数据库操作
|
||||
|
||||
- **创建数据库**: 无需显式创建,当向一个不存在的数据库中的集合插入第一条数据时,该数据库会自动创建。
|
||||
- **查看数据库**: `show dbs`
|
||||
- **切换数据库**: `use myNewDB`
|
||||
- **删除数据库**: 首先切换到要删除的数据库,然后执行 `db.dropDatabase()`。
|
||||
|
||||
---
|
||||
|
||||
## 集合操作
|
||||
|
||||
- **创建集合**:
|
||||
- **隐式创建**: 当向一个不存在的集合插入第一条数据时,集合会自动创建。
|
||||
- **显式创建**: 使用 `db.createCollection()` 方法,可以指定更多选项,如大小限制、验证规则等。
|
||||
```javascript
|
||||
db.createCollection("myCollection", { capped: true, size: 100000 })
|
||||
```
|
||||
|
||||
- **查看集合**: `show collections`
|
||||
|
||||
- **删除集合**: `db.myCollection.drop()`
|
||||
|
||||
---
|
||||
|
||||
## 文档 CRUD 操作
|
||||
|
||||
CRUD 代表创建 (Create)、读取 (Read)、更新 (Update) 和删除 (Delete) 操作。
|
||||
|
||||
### 插入文档 (Create)
|
||||
|
||||
- **`insertOne()`**: 插入单个文档。
|
||||
```javascript
|
||||
db.inventory.insertOne({ item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } })
|
||||
```
|
||||
- **`insertMany()`**: 插入多个文档。
|
||||
```javascript
|
||||
db.inventory.insertMany([
|
||||
{ item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
|
||||
{ item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } }
|
||||
])
|
||||
```
|
||||
|
||||
### 查询文档 (Read)
|
||||
|
||||
- **`find()`**: 查询集合中所有匹配的文档。
|
||||
```javascript
|
||||
// 查询所有文档
|
||||
db.inventory.find({})
|
||||
|
||||
// 查询 qty 大于 50 的文档
|
||||
db.inventory.find({ qty: { $gt: 50 } })
|
||||
```
|
||||
- **`findOne()`**: 只返回匹配的第一个文档。
|
||||
```javascript
|
||||
db.inventory.findOne({ item: "journal" })
|
||||
```
|
||||
|
||||
### 更新文档 (Update)
|
||||
|
||||
- **`updateOne()`**: 更新匹配的第一个文档。
|
||||
```javascript
|
||||
db.inventory.updateOne(
|
||||
{ item: "journal" },
|
||||
{ $set: { "size.uom": "in" }, $currentDate: { lastModified: true } }
|
||||
)
|
||||
```
|
||||
- **`updateMany()`**: 更新所有匹配的文档。
|
||||
```javascript
|
||||
db.inventory.updateMany(
|
||||
{ qty: { $gt: 50 } },
|
||||
{ $set: { "size.uom": "in" }, $currentDate: { lastModified: true } }
|
||||
)
|
||||
```
|
||||
- **`replaceOne()`**: 替换匹配的第一个文档。
|
||||
|
||||
### 删除文档 (Delete)
|
||||
|
||||
- **`deleteOne()`**: 删除匹配的第一个文档。
|
||||
```javascript
|
||||
db.inventory.deleteOne({ item: "journal" })
|
||||
```
|
||||
- **`deleteMany()`**: 删除所有匹配的文档。
|
||||
```javascript
|
||||
db.inventory.deleteMany({ qty: { $gt: 50 } })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实践操作
|
||||
|
||||
### 需求描述
|
||||
|
||||
1. **创建与切换**: 创建一个名为 `bookstore` 的数据库并切换过去。
|
||||
2. **插入数据**: 在 `bookstore` 数据库中创建一个 `books` 集合,并批量插入至少 5 本书的数据,每本书包含 `title`, `author`, `published_year`, `genres` (数组), `stock` (库存) 字段。
|
||||
3. **查询练习**:
|
||||
- 查询所有库存量小于 10 本的书。
|
||||
- 查询所有 `Science Fiction` 类型的书。
|
||||
3. **更新练习**: 将指定一本书的库存量增加 5。
|
||||
4. **删除练习**: 删除所有 `published_year` 在 1950 年之前的书。
|
||||
5. **数据导入导出**: 使用 `mongoexport` 将 `books` 集合导出为 JSON 文件,然后使用 `mongoimport` 将其导入到一个新的集合 `books_backup` 中。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```javascript
|
||||
// 1. 创建与切换数据库
|
||||
use bookstore;
|
||||
|
||||
// 2. 插入数据
|
||||
// 参考 data.js 文件中 Data for: MongoDB基础操作.md 下 books 集合的插入数据部分
|
||||
|
||||
// 3. 查询练习
|
||||
// 查询所有库存量小于 10 本的书
|
||||
db.books.find({ stock: { $lt: 10 } });
|
||||
|
||||
// 查询所有 Science Fiction 类型的书
|
||||
db.books.find({ genres: "Science Fiction" });
|
||||
|
||||
// 4. 更新练习
|
||||
// 将指定一本书的库存量增加 5
|
||||
db.books.updateOne(
|
||||
{ title: "Dune" },
|
||||
{ $inc: { stock: 5 } }
|
||||
);
|
||||
|
||||
// 5. 删除练习
|
||||
// 删除所有 published_year 在 1950 年之前的书
|
||||
db.books.deleteMany({ published_year: { $lt: 1950 } });
|
||||
|
||||
// 6. 数据导入导出
|
||||
// 使用 mongoexport 将 books 集合导出为 JSON 文件
|
||||
// mongoexport --db bookstore --collection books --out books.json --jsonArray
|
||||
|
||||
// 使用 mongoimport 将其导入到一个新的集合 books_backup 中
|
||||
// mongoimport --db bookstore --collection books_backup --file books.json --jsonArray
|
||||
```
|
||||
113
数据库/MongoDB_2025/MongoDB基础概念.md
Normal file
113
数据库/MongoDB_2025/MongoDB基础概念.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 基础概念
|
||||
|
||||
本章节将介绍 NoSQL 数据库的基本概念,并深入探讨 MongoDB 的核心概念、架构原理,为后续的学习打下坚实的基础。
|
||||
|
||||
---
|
||||
|
||||
## NoSQL 数据库概述
|
||||
|
||||
NoSQL(Not Only SQL)泛指非关系型数据库,它们在数据结构、可扩展性和事务模型上与传统的关系型数据库(如 MySQL)有显著不同。
|
||||
|
||||
### 关系型数据库 vs NoSQL 数据库
|
||||
|
||||
| 特性 | 关系型数据库 (RDBMS) | NoSQL 数据库 |
|
||||
| :--- | :--- | :--- |
|
||||
| **数据模型** | 基于表(Table)和行(Row)的结构化数据 | 多样化模型(文档、键值、列族、图) |
|
||||
| **Schema** | 固定(Schema-on-Write),需预先定义表结构 | 动态(Schema-on-Read),数据结构灵活 |
|
||||
| **扩展性** | 主要通过垂直扩展(Scale-Up)提升单机性能 | 主要通过水平扩展(Scale-Out)构建分布式集群 |
|
||||
| **事务** | 遵循 ACID 原则,保证强一致性 | 通常遵循 BASE 原则,保证最终一致性 |
|
||||
| **适用场景** | 事务性要求高、数据结构稳定的应用 | 大数据、高并发、需要快速迭代的应用 |
|
||||
|
||||
### NoSQL 数据库分类
|
||||
|
||||
NoSQL 数据库根据其数据模型的不同,主要分为以下几类:
|
||||
|
||||
1. **文档型数据库 (Document-Oriented)**
|
||||
- **特点**:以文档(通常是 JSON 或 BSON 格式)为单位存储数据,结构灵活。
|
||||
- **代表**:MongoDB, CouchDB
|
||||
|
||||
2. **键值型数据库 (Key-Value)**
|
||||
- **特点**:数据以简单的键值对形式存储,查询速度快。
|
||||
- **代表**:Redis, Memcached
|
||||
|
||||
3. **列族型数据库 (Column-Family)**
|
||||
- **特点**:数据按列族存储,适合大规模数据聚合分析。
|
||||
- **代表**:Cassandra, HBase
|
||||
|
||||
4. **图形型数据库 (Graph)**
|
||||
- **特点**:专注于节点和边的关系,适合社交网络、推荐系统等场景。
|
||||
- **代表**:Neo4j, ArangoDB
|
||||
|
||||
### MongoDB 在 NoSQL 生态中的定位
|
||||
|
||||
MongoDB 是文档型数据库的杰出代表,它凭借其灵活的数据模型、强大的查询语言和高可扩展性,在 NoSQL 生态中占据了重要地位。它特别适用于需要快速开发、数据结构多变、高并发读写的应用场景。
|
||||
|
||||
---
|
||||
|
||||
## 核心概念
|
||||
|
||||
理解 MongoDB 的核心概念是掌握其使用的第一步。
|
||||
|
||||
### 文档(Document)与 BSON 格式
|
||||
|
||||
- **文档 (Document)**:是 MongoDB 中数据的基本单元,由一组键值对(key-value)组成,类似于 JSON 对象。文档的结构是动态的,同一个集合中的文档可以有不同的字段。
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": ObjectId("60c72b2f9b1d8b3b8c8b4567"),
|
||||
"name": "Alice",
|
||||
"age": 30,
|
||||
"email": "alice@example.com",
|
||||
"tags": ["mongodb", "database", "nosql"]
|
||||
}
|
||||
```
|
||||
|
||||
- **BSON (Binary JSON)**:MongoDB 在内部使用 BSON 格式存储文档。BSON 是 JSON 的二进制表示形式,它支持更多的数据类型(如日期、二进制数据),并优化了存储空间和查询性能。
|
||||
|
||||
### 集合(Collection)概念
|
||||
|
||||
- **集合 (Collection)**:是 MongoDB 文档的容器,可以看作是关系型数据库中的表(Table)。但与表不同,集合不需要预先定义结构(schema-less)。
|
||||
|
||||
### 数据库(Database)结构
|
||||
|
||||
- **数据库 (Database)**:是集合的物理容器。一个 MongoDB 实例可以承载多个数据库,每个数据库都有自己独立的权限和文件。
|
||||
|
||||
### MongoDB 与关系型数据库术语对比
|
||||
|
||||
| MongoDB | 关系型数据库 (RDBMS) |
|
||||
| :--- | :--- |
|
||||
| Database | Database |
|
||||
| Collection | Table |
|
||||
| Document | Row (or Record) |
|
||||
| Field | Column (or Attribute) |
|
||||
| Index | Index |
|
||||
| Replica Set | Master-Slave Replication |
|
||||
| Sharding | Partitioning |
|
||||
|
||||
---
|
||||
|
||||
## 架构原理
|
||||
|
||||
了解 MongoDB 的底层架构有助于我们更好地进行性能调优和故障排查。
|
||||
|
||||
### 存储引擎(WiredTiger)
|
||||
|
||||
WiredTiger 是 MongoDB 默认的存储引擎,它提供了文档级别的并发控制、数据压缩和快照等高级功能,是 MongoDB 高性能的关键。
|
||||
|
||||
### 内存映射文件系统
|
||||
|
||||
MongoDB 使用内存映射文件(Memory-Mapped Files)来处理数据。它将磁盘上的数据文件映射到内存中,使得 MongoDB 可以像访问内存一样访问数据,从而将数据管理委托给操作系统的虚拟内存管理器,简化了代码并提升了性能。
|
||||
|
||||
### 索引机制
|
||||
|
||||
为了提高查询效率,MongoDB 支持在任意字段上创建索引。与关系型数据库类似,MongoDB 的索引也采用 B-Tree 数据结构,能够极大地加速数据检索过程。
|
||||
|
||||
### 查询优化器
|
||||
|
||||
MongoDB 内置了一个查询优化器,它会分析查询请求,并从多个可能的查询计划中选择一个最优的执行计划,以确保查询的高效性。
|
||||
|
||||
---
|
||||
|
||||
## 文档学习
|
||||
|
||||
- 阅读 MongoDB 官方文档中关于“核心概念”的部分。
|
||||
215
数据库/MongoDB_2025/MongoDB备份恢复.md
Normal file
215
数据库/MongoDB_2025/MongoDB备份恢复.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# MongoDB 备份与恢复
|
||||
|
||||
制定可靠的备份和恢复策略是数据库管理中至关重要的一环,它可以帮助在发生数据损坏、误操作或灾难性故障时恢复数据。本章节将介绍 MongoDB 常用的备份方法、恢复流程以及灾难恢复的最佳实践。
|
||||
|
||||
---
|
||||
|
||||
## 备份策略
|
||||
|
||||
选择哪种备份方法取决于具体需求,如恢复时间目标 (RTO)、恢复点目标 (RPO)、数据库规模以及部署架构(独立实例、副本集、分片集群)。
|
||||
|
||||
### 备份方法
|
||||
|
||||
1. **`mongodump` 和 `mongorestore`**
|
||||
- **描述**: MongoDB 提供的官方命令行工具,用于创建数据库的 BSON 文件备份,并可以从这些文件恢复数据。
|
||||
- **优点**: 简单易用,适合小型数据集或对备份窗口要求不高的场景。
|
||||
- **缺点**: 备份期间可能会影响数据库性能。对于大型数据集,备份和恢复过程可能非常耗时。在恢复 `mongodump` 的备份时,它会重建索引,这会增加恢复时间。
|
||||
|
||||
2. **文件系统快照 (Filesystem Snapshots)**
|
||||
- **描述**: 利用文件系统(如 LVM)或云存储(如 AWS EBS)的快照功能,对 MongoDB 的数据文件进行即时备份。
|
||||
- **优点**: 备份速度快,对数据库性能影响小。恢复速度也很快,因为数据和索引都无需重建。
|
||||
- **缺点**: 必须确保在创建快照时,数据处于一致性状态。这通常需要开启 journaling 并确保在快照前执行 `fsync` 和 lock 操作。
|
||||
|
||||
3. **MongoDB Cloud Manager / Ops Manager**
|
||||
- **描述**: MongoDB 提供的企业级管理工具,提供持续的、时间点恢复的备份服务。
|
||||
- **优点**: 提供图形化界面,管理方便。支持副本集和分片集群的持续备份和时间点恢复,可以恢复到任意时刻。
|
||||
- **缺点**: 是商业服务,需要额外成本。
|
||||
|
||||
---
|
||||
|
||||
## 使用 `mongodump` 和 `mongorestore`
|
||||
|
||||
### `mongodump`
|
||||
|
||||
`mongodump` 可以导出整个数据库、特定数据库或特定集合。
|
||||
|
||||
```shell
|
||||
# 备份整个 MongoDB 实例
|
||||
mongodump --out /data/backup/`date +%F`
|
||||
|
||||
# 备份指定的数据库
|
||||
mongodump --db myAppDB --out /data/backup/myAppDB
|
||||
|
||||
# 备份指定的集合
|
||||
mongodump --db myAppDB --collection users --out /data/backup/users_collection
|
||||
|
||||
# 备份远程数据库并启用压缩
|
||||
mongodump --host mongodb.example.com --port 27017 --username myUser --password myPass --authenticationDatabase admin --db myAppDB --gzip --out /data/backup/myAppDB.gz
|
||||
```
|
||||
|
||||
### `mongorestore`
|
||||
|
||||
`mongorestore` 用于将 `mongodump` 创建的备份恢复到数据库中。
|
||||
|
||||
```shell
|
||||
# 恢复整个实例的备份
|
||||
mongorestore /data/backup/2023-10-27/
|
||||
|
||||
# 恢复指定的数据库备份
|
||||
# mongorestore 会将数据恢复到备份时同名的数据库中
|
||||
mongorestore --db myAppDB /data/backup/myAppDB/
|
||||
|
||||
# 恢复并删除现有数据
|
||||
# 使用 --drop 选项可以在恢复前删除目标集合
|
||||
mongorestore --db myAppDB --collection users --drop /data/backup/users_collection/users.bson
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 副本集和分片集群的备份
|
||||
|
||||
### 备份副本集
|
||||
|
||||
- **使用 `mongodump`**: 建议连接到一个从节点进行备份,以减少对主节点的影响。可以使用 `--oplog` 选项来创建一个包含 oplog 条目的时间点快照,这对于在恢复后与其他副本集成员同步非常重要。
|
||||
|
||||
```bash
|
||||
mongodump --host secondary.example.com --oplog --out /data/backup/repl_set_backup
|
||||
```
|
||||
|
||||
- **使用文件系统快照**: 可以在一个被停止(或锁定)的从节点上进行,以保证数据一致性。
|
||||
|
||||
### 备份分片集群
|
||||
|
||||
备份分片集群要复杂得多,因为必须保证整个集群的数据是一致的。
|
||||
|
||||
- **核心挑战**: 必须在同一时刻捕获所有分片和配置服务器的数据快照。
|
||||
- **推荐方法**: **强烈建议使用 MongoDB Cloud Manager 或 Ops Manager** 来处理分片集群的备份,因为它们专门设计用于处理这种复杂性。
|
||||
- **手动备份(不推荐,风险高)**:
|
||||
1. 禁用 Balancer。
|
||||
2. 对配置服务器进行 `mongodump`。
|
||||
3. 对每个分片副本集进行 `mongodump`。
|
||||
4. 重新启用 Balancer。
|
||||
恢复过程同样复杂且容易出错。
|
||||
|
||||
---
|
||||
|
||||
## 恢复策略与灾难恢复
|
||||
|
||||
### 恢复误操作的数据
|
||||
|
||||
- **使用延迟从节点**: 如果配置了一个延迟从节点,可以停止它的复制,从其数据文件中恢复误删或误改的数据。
|
||||
- **使用时间点恢复**: 如果使用 Cloud Manager / Ops Manager,可以将数据库恢复到误操作发生前的任意时间点。
|
||||
|
||||
### 灾难恢复 (Disaster Recovery)
|
||||
|
||||
灾难恢复计划旨在应对整个数据中心或区域级别的故障。
|
||||
|
||||
- **异地备份**: 将备份数据存储在与生产数据中心物理位置不同的地方。
|
||||
- **多数据中心部署**: 将副本集的成员分布在不同的地理位置。例如,一个主节点和从节点在主数据中心,另一个从节点在灾备数据中心。当主数据中心发生故障时,可以手动或自动将灾备中心的从节点提升为新的主节点。
|
||||
|
||||
---
|
||||
|
||||
## 实践操作
|
||||
|
||||
### 需求描述
|
||||
|
||||
构建一个完整的 MongoDB 备份与恢复实践环境,掌握不同场景下的备份策略和恢复操作。通过实际操作理解备份工具的使用方法、副本集备份的最佳实践,以及制定符合业务需求的备份恢复策略。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```shell
|
||||
# 1. 准备测试数据
|
||||
# 创建测试数据库和集合
|
||||
use myAppDB
|
||||
db.users.insertMany([
|
||||
{name: "Alice", age: 25, email: "alice@example.com"},
|
||||
{name: "Bob", age: 30, email: "bob@example.com"},
|
||||
{name: "Charlie", age: 35, email: "charlie@example.com"}
|
||||
])
|
||||
|
||||
db.orders.insertMany([
|
||||
{orderId: "ORD001", userId: "Alice", amount: 100.50, status: "completed"},
|
||||
{orderId: "ORD002", userId: "Bob", amount: 250.75, status: "pending"},
|
||||
{orderId: "ORD003", userId: "Charlie", amount: 89.99, status: "completed"}
|
||||
])
|
||||
|
||||
# 验证数据插入
|
||||
db.users.countDocuments() # 预期结果: 3
|
||||
db.orders.countDocuments() # 预期结果: 3
|
||||
|
||||
# 2. 使用 mongodump 备份数据库
|
||||
# 创建备份目录
|
||||
mkdir -pv /data/mongodb/backup
|
||||
|
||||
# 备份整个 myAppDB 数据库
|
||||
mongodump --db myAppDB --out /data/mongodb/backup/$(date +%F)/dbs/
|
||||
|
||||
# 验证备份文件
|
||||
ls -la /data/mongodb/backup/$(date +%F)/dbs/
|
||||
# 预期结果: 应该看到 users.bson, users.metadata.json, orders.bson, orders.metadata.json 等文件
|
||||
|
||||
# 备份指定集合(带压缩)
|
||||
mongodump --db myAppDB --collection users --gzip --out /data/mongodb/backup/$(date +%F)/collections/
|
||||
|
||||
# 验证压缩备份
|
||||
ls -la /data/mongodb/backup/$(date +%F)/users_backup/myAppDB/
|
||||
# 预期结果: 应该看到 users.bson.gz 和 users.metadata.json.gz 文件
|
||||
|
||||
# 3. 模拟数据丢失并恢复
|
||||
# 删除 users 集合模拟数据丢失
|
||||
use myAppDB
|
||||
db.users.drop()
|
||||
|
||||
# 验证数据已删除
|
||||
db.users.countDocuments() # 预期结果: 0
|
||||
show collections # 预期结果: 只显示 orders 集合
|
||||
|
||||
# 使用 mongorestore 恢复 users 集合
|
||||
gzip /data/mongodb/backup/$(date +%F)/collections/myAppDB/users.bson.gz -d /data/mongodb/backup/$(date +%F)/collections/myAppDB/
|
||||
mongorestore --db myAppDB --collection users /data/mongodb/backup/$(date +%F)/collections/myAppDB/users.bson
|
||||
|
||||
# 验证数据恢复
|
||||
db.users.countDocuments() # 预期结果: 3
|
||||
db.users.find().pretty() # 预期结果: 显示所有用户数据
|
||||
|
||||
# 4. 完整数据库恢复测试
|
||||
# 删除整个数据库
|
||||
use myAppDB
|
||||
db.dropDatabase()
|
||||
|
||||
# 验证数据库已删除
|
||||
show dbs # 预期结果: myAppDB 不在列表中
|
||||
|
||||
# 恢复整个数据库
|
||||
mongorestore /data/mongodb/backup/$(date +%F)/dbs/myAppDB/
|
||||
|
||||
# 验证数据库恢复
|
||||
use myAppDB
|
||||
show collections # 预期结果: 显示 users 和 orders 集合
|
||||
db.users.countDocuments() # 预期结果: 3
|
||||
db.orders.countDocuments() # 预期结果: 3
|
||||
|
||||
# 5. 副本集备份实践(如果已配置副本集)
|
||||
# 连接到副本集的从节点进行备份
|
||||
mongodump --host secondary.example.com:27017 --oplog --out /data/backup/replica_backup
|
||||
|
||||
# 验证副本集备份
|
||||
ls -la /data/backup/replica_backup/
|
||||
# 预期结果: 应该看到各个数据库目录和 oplog.bson 文件
|
||||
|
||||
# 6.[扩展] 制定生产环境备份策略
|
||||
# 电商订单数据库备份策略设计
|
||||
|
||||
# 业务需求分析:
|
||||
# - RTO (恢复时间目标): 4小时
|
||||
# - RPO (恢复点目标): 1小时
|
||||
# - 数据重要性: 订单数据极其重要,用户数据重要
|
||||
# - 业务特点: 24/7运行,高并发读写
|
||||
|
||||
# 推荐备份策略:
|
||||
# 1. 每日全量备份 (使用文件系统快照)
|
||||
# 2. 每小时增量备份 (使用 oplog)
|
||||
# 3. 异地备份存储
|
||||
# 4. 副本集跨数据中心部署
|
||||
|
||||
```
|
||||
273
数据库/MongoDB_2025/MongoDB安全管理.md
Normal file
273
数据库/MongoDB_2025/MongoDB安全管理.md
Normal file
@@ -0,0 +1,273 @@
|
||||
# 安全管理
|
||||
|
||||
确保数据库的安全是任何应用都必须考虑的关键问题。MongoDB 提供了丰富的安全特性,包括认证、授权、加密等,以保护数据免受未经授权的访问。本章节将详细介绍如何配置和管理 MongoDB 的各项安全功能。
|
||||
|
||||
---
|
||||
|
||||
## 安全概述
|
||||
|
||||
MongoDB 的安全模型主要围绕以下几个核心概念构建:
|
||||
|
||||
- **认证 (Authentication)**: 验证用户身份,确认“你是谁”。
|
||||
- **授权 (Authorization)**: 控制经过认证的用户可以执行哪些操作,确认“你能做什么”。
|
||||
- **加密 (Encryption)**: 保护数据在传输过程(TLS/SSL)和静态存储时(Encryption at Rest)的安全。
|
||||
- **审计 (Auditing)**: 记录数据库上发生的活动,以便进行安全分析和合规性检查。
|
||||
|
||||
默认情况下,MongoDB 的安全特性是**未启用**的。必须显式地配置和启用它们。
|
||||
|
||||
---
|
||||
|
||||
## 认证机制 (Authentication)
|
||||
|
||||
启用认证是保护数据库的第一步。当认证启用后,所有客户端和数据库节点之间的连接都必须提供有效的凭据。
|
||||
|
||||
### 启用认证
|
||||
|
||||
在 `mongod.conf` 配置文件中或通过命令行参数启用认证:
|
||||
|
||||
```yaml
|
||||
# mongod.conf
|
||||
security:
|
||||
authorization: enabled
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```bash
|
||||
mongod --auth
|
||||
```
|
||||
|
||||
### 认证方法
|
||||
|
||||
MongoDB 支持多种认证机制,最常用的是 **SCRAM (Salted Challenge Response Authentication Mechanism)**。
|
||||
|
||||
- **SCRAM-SHA-1**: 默认机制。
|
||||
- **SCRAM-SHA-256**: 更强的加密算法,建议在 MongoDB 4.0 及以上版本中使用。
|
||||
|
||||
### 创建管理员用户
|
||||
|
||||
在启用认证之前,必须先创建一个具有 `userAdminAnyDatabase` 角色的用户管理员。这个用户将用于创建和管理其他用户。
|
||||
|
||||
1. **以无认证模式启动 `mongod`**。
|
||||
2. **连接到 `admin` 数据库并创建用户**:
|
||||
|
||||
```javascript
|
||||
use admin
|
||||
db.createUser({
|
||||
user: "myUserAdmin",
|
||||
pwd: passwordPrompt(), // or a plain-text password
|
||||
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
|
||||
})
|
||||
```
|
||||
|
||||
3. **重启 `mongod` 并启用认证**。
|
||||
|
||||
---
|
||||
|
||||
## 授权与基于角色的访问控制 (RBAC)
|
||||
|
||||
MongoDB 使用基于角色的访问控制(Role-Based Access Control, RBAC)来管理用户权限。权限被定义为**角色 (Role)**,然后将角色分配给用户。
|
||||
|
||||
### 内置角色 (Built-In Roles)
|
||||
|
||||
MongoDB 提供了一系列预定义的角色,涵盖了常见的管理和操作需求。
|
||||
|
||||
- **数据库用户角色**: `read`, `readWrite`
|
||||
- **数据库管理员角色**: `dbAdmin`, `dbOwner`, `userAdmin`
|
||||
- **集群管理员角色**: `clusterAdmin`, `clusterManager`, `hostManager`
|
||||
- **备份与恢复角色**: `backup`, `restore`
|
||||
- **所有数据库角色**: `readAnyDatabase`, `readWriteAnyDatabase`, `userAdminAnyDatabase`, `dbAdminAnyDatabase`
|
||||
- **超级用户角色**: `root` (拥有所有权限)
|
||||
|
||||
### 自定义角色 (Custom Roles)
|
||||
|
||||
如果内置角色无法满足精细化权限控制需求,可以创建自定义角色。
|
||||
|
||||
```javascript
|
||||
use myAppDB
|
||||
db.createRole({
|
||||
role: "salesDataViewer",
|
||||
privileges: [
|
||||
{ resource: { db: "myAppDB", collection: "sales" }, actions: ["find"] }
|
||||
],
|
||||
roles: []
|
||||
})
|
||||
```
|
||||
|
||||
### 管理用户和角色
|
||||
|
||||
- `db.createUser()`: 创建用户。
|
||||
- `db.updateUser()`: 更新用户信息(如密码、角色)。
|
||||
- `db.dropUser()`: 删除用户。
|
||||
- `db.createRole()`: 创建角色。
|
||||
- `db.grantRolesToUser()`: 为用户授予角色。
|
||||
- `db.revokeRolesFromUser()`: 撤销用户的角色。
|
||||
|
||||
---
|
||||
|
||||
## 网络加密 (TLS/SSL)
|
||||
|
||||
为了保护数据在网络传输过程中的安全,防止窃听和中间人攻击,应该为 MongoDB 部署启用 TLS/SSL 加密。
|
||||
|
||||
### 配置 TLS/SSL
|
||||
|
||||
1. **获取 TLS/SSL 证书**: 可以使用自签名证书(用于内部测试)或从证书颁发机构 (CA) 获取证书。
|
||||
2. **配置 `mongod` 和 `mongos`**: 在配置文件中指定证书文件、私钥文件和 CA 文件。
|
||||
|
||||
```yaml
|
||||
net:
|
||||
tls:
|
||||
mode: requireTLS
|
||||
certificateKeyFile: /path/to/mongodb.pem
|
||||
CAFile: /path/to/ca.pem
|
||||
```
|
||||
|
||||
3. **客户端连接**: 客户端在连接时也需要指定 TLS/SSL 选项。
|
||||
|
||||
```shell
|
||||
mongo --ssl --sslCAFile /path/to/ca.pem --sslPEMKeyFile /path/to/client.pem ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 静态数据加密 (Encryption at Rest)
|
||||
|
||||
对于高度敏感的数据,除了网络加密,还应该考虑对存储在磁盘上的数据文件进行加密。
|
||||
|
||||
- **WiredTiger 的原生加密**: MongoDB Enterprise 版本支持使用 WiredTiger 存储引擎的原生加密功能。它使用本地密钥管理器或第三方密钥管理服务(如 KMIP)来管理加密密钥。
|
||||
- **文件系统/磁盘加密**: 也可以在操作系统层面或通过云服务商提供的功能(如 AWS KMS, Azure Key Vault)对存储设备进行加密。
|
||||
|
||||
---
|
||||
|
||||
## 实践操作
|
||||
|
||||
### 需求描述
|
||||
|
||||
构建一个完整的MongoDB安全管理环境,模拟企业级应用的安全需求。该场景需要配置认证机制、创建不同权限的用户角色,并验证安全策略的有效性。通过实际操作来理解MongoDB安全管理的核心概念和最佳实践。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```shell
|
||||
# 1. 启用认证并创建管理员用户
|
||||
# 首先以无认证模式启动MongoDB
|
||||
mongod --dbpath /data/mongodb/ins11 --port 27117 --fork --logpath /data/mongodb/logs/ins11.log
|
||||
|
||||
# 连接到MongoDB并创建管理员用户
|
||||
mongosh --port 27117
|
||||
# 在mongosh中执行:
|
||||
use admin
|
||||
db.createUser({
|
||||
user: "admin",
|
||||
pwd: "ealgeslab123",
|
||||
roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
|
||||
})
|
||||
|
||||
# 验证管理员用户创建成功
|
||||
db.getUsers()
|
||||
# 预期结果:显示创建的admin用户信息
|
||||
|
||||
# 2. 重启MongoDB并启用认证
|
||||
# 停止MongoDB服务
|
||||
db.adminCommand("shutdown")
|
||||
|
||||
# 以认证模式重新启动
|
||||
mongod --auth --dbpath /data/mongodb/ins11 --port 27117 --fork --logpath /data/mongodb/logs/ins11.log
|
||||
|
||||
# 使用管理员账户登录
|
||||
mongosh --port 27117 -u "admin" -p "ealgeslab123" --authenticationDatabase "admin"
|
||||
|
||||
# 3. 创建业务数据库和用户
|
||||
# 创建应用数据库
|
||||
use ecommerce
|
||||
|
||||
# 创建具有读写权限的业务用户
|
||||
db.createUser({
|
||||
user: "appUser",
|
||||
pwd: "appPassword123",
|
||||
roles: [{ role: "readWrite", db: "ecommerce" }]
|
||||
})
|
||||
|
||||
# 验证业务用户创建成功
|
||||
db.getUsers()
|
||||
# 预期结果:显示appUser用户信息
|
||||
|
||||
# 4. 创建自定义角色
|
||||
# 创建只允许查询和更新特定集合的自定义角色
|
||||
db.createRole({
|
||||
role: "productManager",
|
||||
privileges: [
|
||||
{
|
||||
resource: { db: "ecommerce", collection: "products" },
|
||||
actions: ["find", "update"]
|
||||
}
|
||||
],
|
||||
roles: []
|
||||
})
|
||||
|
||||
# 创建使用自定义角色的用户
|
||||
db.createUser({
|
||||
user: "productAdmin",
|
||||
pwd: "productPassword123",
|
||||
roles: [{ role: "productManager", db: "ecommerce" }]
|
||||
})
|
||||
|
||||
# 验证自定义角色和用户
|
||||
db.getRoles({ showPrivileges: true })
|
||||
db.getUsers()
|
||||
# 预期结果:显示productManager角色和productAdmin用户
|
||||
|
||||
# 5. 测试用户权限
|
||||
# 使用业务用户连接
|
||||
mongosh --port 27117 -u "appUser" -p "appPassword123" --authenticationDatabase "ecommerce"
|
||||
|
||||
# 测试读写权限
|
||||
use ecommerce
|
||||
db.products.insertOne({ name: "Laptop", price: 999, category: "Electronics" })
|
||||
db.products.find()
|
||||
db.products.updateOne({ name: "Laptop" }, { $set: { price: 899 } })
|
||||
# 预期结果:所有操作成功执行
|
||||
|
||||
# 6. 测试自定义角色权限
|
||||
# 使用自定义角色用户连接
|
||||
mongosh --port 27017 -u "productAdmin" -p "productPassword123" --authenticationDatabase "ecommerce"
|
||||
|
||||
use ecommerce
|
||||
# 测试允许的操作
|
||||
db.products.find()
|
||||
db.products.updateOne({ name: "Laptop" }, { $set: { description: "High-performance laptop" } })
|
||||
# 预期结果:查询和更新操作成功
|
||||
|
||||
# 测试禁止的操作
|
||||
db.products.insertOne({ name: "Mouse", price: 25 })
|
||||
# 预期结果:操作失败,提示权限不足
|
||||
|
||||
db.products.deleteOne({ name: "Laptop" })
|
||||
# 预期结果:操作失败,提示权限不足
|
||||
|
||||
# 8. 查看用户和角色信息
|
||||
# 查看所有用户
|
||||
use admin
|
||||
db.system.users.find().pretty()
|
||||
|
||||
# 查看所有角色
|
||||
db.system.roles.find().pretty()
|
||||
|
||||
# 查看当前用户权限
|
||||
db.runCommand({ connectionStatus: 1 })
|
||||
# 预期结果:显示当前连接的用户信息和权限
|
||||
|
||||
# 9. 权限管理操作
|
||||
use ecommerce
|
||||
# 为用户添加新角色
|
||||
db.grantRolesToUser("appUser", [{ role: "dbAdmin", db: "ecommerce" }])
|
||||
|
||||
# 撤销用户角色
|
||||
db.revokeRolesFromUser("appUser", [{ role: "dbAdmin", db: "ecommerce" }])
|
||||
|
||||
# 修改用户密码
|
||||
db.updateUser("appUser", { pwd: "newPassword123" })
|
||||
|
||||
# 验证权限变更
|
||||
db.getUser("appUser")
|
||||
# 预期结果:显示用户的最新角色信息
|
||||
```
|
||||
300
数据库/MongoDB_2025/MongoDB性能优化.md
Normal file
300
数据库/MongoDB_2025/MongoDB性能优化.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# MongoDB 性能监控与调优
|
||||
|
||||
为了确保 MongoDB 数据库持续、稳定、高效地运行,必须对其进行有效的性能监控和及时的调优。本章节将介绍 MongoDB 的关键性能指标、常用的监控工具以及针对常见性能问题的调优策略。
|
||||
|
||||
---
|
||||
|
||||
## 关键性能指标
|
||||
|
||||
监控 MongoDB 时,应重点关注以下几类指标:
|
||||
|
||||
### 操作计数器 (Operation Counters)
|
||||
|
||||
- **`opcounters`**: 显示自 `mongod` 启动以来执行的数据库操作(insert, query, update, delete, getmore, command)的总数。通过观察其增长率,可以了解数据库的负载情况。
|
||||
|
||||
### 锁 (Locks)
|
||||
|
||||
- **`globalLock`**: 反映全局锁的使用情况。在 WiredTiger 存储引擎中,全局锁的使用已大大减少,但仍需关注。
|
||||
- **`locks`**: 提供数据库、集合等更细粒度锁的信息。长时间的锁等待(`timeAcquiringMicros`)可能表示存在锁竞争,需要优化查询或索引。
|
||||
|
||||
### 网络 (Network)
|
||||
|
||||
- **`network.bytesIn` / `network.bytesOut`**: 进出数据库的网络流量。
|
||||
- **`network.numRequests`**: 接收到的请求总数。
|
||||
- 监控网络指标有助于发现网络瓶颈或异常的客户端行为。
|
||||
|
||||
### 内存 (Memory)
|
||||
|
||||
- **`mem.resident`**: 进程占用的物理内存(RAM)大小。
|
||||
- **`mem.virtual`**: 进程使用的虚拟内存大小。
|
||||
- **`mem.mapped`**: 内存映射文件的大小。
|
||||
- 监控内存使用情况,特别是 WiredTiger 的内部缓存(`wiredTiger.cache`),对于确保性能至关重要。
|
||||
|
||||
### Oplog
|
||||
|
||||
- **Oplog Window**: Oplog 中记录的操作所覆盖的时间范围。如果 oplog window 太小,从节点可能会因为跟不上主节点的更新速度而掉队(stale)。
|
||||
|
||||
### 慢查询 (Slow Queries)
|
||||
|
||||
- **`system.profile` 集合**: 当开启数据库分析(profiling)后,执行时间超过阈值的查询会被记录到该集合中。这是识别和优化慢查询的主要工具。
|
||||
|
||||
---
|
||||
|
||||
## 监控工具
|
||||
|
||||
### `mongostat`
|
||||
|
||||
- **描述**: 一个命令行工具,可以实时地、逐秒地显示 MongoDB 实例的主要性能指标,类似于 Linux 的 `vmstat`。
|
||||
- **用途**: 快速了解数据库当前的操作负载和性能状况。
|
||||
|
||||
```bash
|
||||
mongostat
|
||||
```
|
||||
|
||||
### `mongotop`
|
||||
|
||||
- **描述**: 一个命令行工具,用于跟踪 MongoDB 实例花费时间最多的读写操作。
|
||||
- **用途**: 快速定位哪些集合是当前系统的性能热点。
|
||||
|
||||
```bash
|
||||
mongotop 10 # 每 10 秒刷新一次
|
||||
```
|
||||
|
||||
### `db.serverStatus()`
|
||||
|
||||
- **描述**: 一个数据库命令,返回一个包含大量服务器状态信息的文档。
|
||||
- **用途**: 获取详细的、全面的性能指标,是大多数监控系统的主要数据来源。
|
||||
|
||||
```javascript
|
||||
db.serverStatus()
|
||||
```
|
||||
|
||||
### MongoDB Cloud Manager / Ops Manager
|
||||
|
||||
- **描述**: 提供了一个功能强大的图形化监控界面,可以收集、可视化并告警各种性能指标。
|
||||
- **用途**: 长期、系统化的性能监控和趋势分析。
|
||||
|
||||
---
|
||||
|
||||
## 性能调优策略
|
||||
|
||||
### 索引优化
|
||||
|
||||
- **识别缺失的索引**: 通过分析 `system.profile` 集合中的慢查询日志,找出那些因为缺少合适索引而导致全集合扫描(`COLLSCAN`)的查询。
|
||||
- **评估现有索引**: 使用 `$indexStats` 聚合阶段检查索引的使用频率。对于很少使用或从不使用的索引,应考虑删除以减少写操作的开销和存储空间。
|
||||
- **创建复合索引**: 根据 ESR 法则设计高效的复合索引,使其能够覆盖多种查询模式。
|
||||
|
||||
### 查询优化
|
||||
|
||||
- **使用投影 (Projection)**: 只返回查询所需的字段,减少网络传输量和客户端处理数据的负担。
|
||||
- **避免使用 `$where` 和 `$function`**: 这类操作无法使用索引,并且会在每个文档上执行 JavaScript,性能极差。
|
||||
- **优化正则表达式**: 尽量使用前缀表达式(如 `/^prefix/`),这样可以利用索引。避免使用不区分大小写的正则表达式,因为它无法有效利用索引。
|
||||
|
||||
### Schema 设计调优
|
||||
|
||||
- **遵循数据建模模式**: 根据应用的读写模式选择合适的建模策略(嵌入 vs. 引用),可以从根本上提升性能。
|
||||
- **避免大文档和无界数组**: 过大的文档会增加内存使用和网络传输,无限制增长的数组会给更新操作带来性能问题。
|
||||
|
||||
### 硬件和拓扑结构调优
|
||||
|
||||
- **内存**: 确保 WiredTiger 的缓存大小(默认为 `(RAM - 1GB) / 2`)足够容纳工作集(Working Set),即最常访问的数据和索引。
|
||||
- **磁盘**: 使用 SSD 可以显著提升 I/O 性能,特别是对于写密集型应用。
|
||||
- **网络**: 确保数据库服务器之间的网络延迟尽可能低,特别是在分片集群和地理分布的副本集中。
|
||||
- **读写分离**: 在读密集型应用中,通过将读请求路由到从节点来扩展读取能力。
|
||||
|
||||
---
|
||||
|
||||
## 实践操作
|
||||
|
||||
### 需求描述
|
||||
|
||||
构建一个完整的 MongoDB 性能监控与调优实践环境,掌握性能指标监控、慢查询分析、索引优化等核心技能。通过实际操作理解如何识别性能瓶颈、分析查询执行计划、创建高效索引,以及使用各种监控工具来持续优化数据库性能。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```shell
|
||||
# 1. 准备测试数据和环境
|
||||
# 创建性能测试数据库
|
||||
use performanceTestDB
|
||||
|
||||
# 创建大量测试数据
|
||||
for (let i = 0; i < 100000; i++) {
|
||||
db.products.insertOne({
|
||||
productId: "PROD" + i.toString().padStart(6, '0'),
|
||||
name: "Product " + i,
|
||||
category: ["electronics", "books", "clothing", "home"][i % 4],
|
||||
price: Math.floor(Math.random() * 1000) + 10,
|
||||
rating: Math.floor(Math.random() * 5) + 1,
|
||||
inStock: Math.random() > 0.3,
|
||||
tags: ["popular", "new", "sale", "featured"].slice(0, Math.floor(Math.random() * 3) + 1),
|
||||
createdAt: new Date(Date.now() - Math.floor(Math.random() * 365 * 24 * 60 * 60 * 1000))
|
||||
});
|
||||
}
|
||||
|
||||
# 验证数据插入
|
||||
db.products.countDocuments() # 预期结果: 100000
|
||||
|
||||
# 2. 开启数据库性能分析 (Profiling)
|
||||
# 设置慢查询阈值为100ms,记录所有慢查询
|
||||
db.setProfilingLevel(1, { slowms: 100 })
|
||||
|
||||
# 验证profiling设置
|
||||
db.getProfilingStatus()
|
||||
# 预期结果: { "was" : 1, "slowms" : 100, "sampleRate" : 1.0 }
|
||||
|
||||
# 3. 生成慢查询进行性能分析
|
||||
# 执行没有索引的复杂查询(故意制造慢查询)
|
||||
db.products.find({
|
||||
category: "electronics",
|
||||
price: { $gte: 500 },
|
||||
rating: { $gte: 4 },
|
||||
inStock: true
|
||||
}).sort({ createdAt: -1 }).limit(10)
|
||||
|
||||
# 执行正则表达式查询(通常较慢)
|
||||
db.products.find({ name: /Product 1.*/ })
|
||||
|
||||
# 执行范围查询
|
||||
db.products.find({
|
||||
price: { $gte: 100, $lte: 500 },
|
||||
createdAt: { $gte: new Date("2023-01-01") }
|
||||
})
|
||||
|
||||
# 4. 分析慢查询日志
|
||||
# 查看最近的慢查询记录
|
||||
db.system.profile.find().sort({ ts: -1 }).limit(5).pretty()
|
||||
# 预期结果: 显示最近5条慢查询记录,包含执行时间、查询语句等信息
|
||||
|
||||
# 查看特定类型的慢查询
|
||||
db.system.profile.find({
|
||||
"command.find": "products",
|
||||
"millis": { $gte: 100 }
|
||||
}).sort({ ts: -1 })
|
||||
|
||||
# 统计慢查询数量
|
||||
db.system.profile.countDocuments({ "millis": { $gte: 100 } })
|
||||
# 预期结果: 显示慢查询总数
|
||||
|
||||
# 5. 使用 explain() 分析查询执行计划
|
||||
# 分析复杂查询的执行计划
|
||||
db.products.find({
|
||||
category: "electronics",
|
||||
price: { $gte: 500 },
|
||||
rating: { $gte: 4 }
|
||||
}).explain("executionStats")
|
||||
# 预期结果: 显示详细的执行统计,包括扫描的文档数、执行时间等
|
||||
|
||||
# 查看查询是否使用了索引
|
||||
db.products.find({ category: "electronics" }).explain("queryPlanner")
|
||||
# 预期结果: winningPlan.stage 应该显示 "COLLSCAN"(全集合扫描)
|
||||
|
||||
# 6. 创建索引优化查询性能
|
||||
# 为常用查询字段创建单字段索引
|
||||
db.products.createIndex({ category: 1 })
|
||||
db.products.createIndex({ price: 1 })
|
||||
db.products.createIndex({ rating: 1 })
|
||||
db.products.createIndex({ createdAt: -1 })
|
||||
|
||||
# 创建复合索引(遵循ESR法则:Equality, Sort, Range)
|
||||
db.products.createIndex({
|
||||
category: 1,
|
||||
createdAt: -1,
|
||||
price: 1
|
||||
})
|
||||
|
||||
# 创建文本索引用于搜索
|
||||
db.products.createIndex({ name: "text", tags: "text" })
|
||||
|
||||
# 验证索引创建
|
||||
db.products.getIndexes()
|
||||
# 预期结果: 显示所有已创建的索引
|
||||
|
||||
# 7. 验证索引优化效果
|
||||
# 重新执行之前的慢查询,观察性能提升
|
||||
db.products.find({
|
||||
category: "electronics",
|
||||
price: { $gte: 500 },
|
||||
rating: { $gte: 4 }
|
||||
}).sort({ createdAt: -1 }).explain("executionStats")
|
||||
# 预期结果: 应该显示 "IXSCAN"(索引扫描),执行时间显著减少
|
||||
|
||||
# 比较优化前后的查询性能
|
||||
var startTime = new Date();
|
||||
db.products.find({ category: "electronics", price: { $gte: 500 } }).toArray();
|
||||
var endTime = new Date();
|
||||
print("查询执行时间: " + (endTime - startTime) + "ms");
|
||||
# 预期结果: 执行时间应该显著减少
|
||||
|
||||
# 8. 使用监控工具进行性能监控
|
||||
# 在终端中使用 mongostat 监控实时性能
|
||||
# mongostat --host localhost:27017
|
||||
# 预期结果: 显示实时的操作统计、内存使用、网络流量等
|
||||
|
||||
# 使用 mongotop 监控集合级别的读写活动
|
||||
# mongotop 10
|
||||
# 预期结果: 每10秒显示各集合的读写时间统计
|
||||
|
||||
# 9. 服务器状态监控
|
||||
# 获取详细的服务器状态信息
|
||||
db.serverStatus()
|
||||
# 预期结果: 返回包含操作计数器、锁信息、内存使用、网络统计等的详细报告
|
||||
|
||||
# 监控特定的性能指标
|
||||
db.serverStatus().opcounters
|
||||
# 预期结果: 显示各种操作(insert、query、update等)的计数
|
||||
|
||||
db.serverStatus().wiredTiger.cache
|
||||
# 预期结果: 显示WiredTiger缓存的使用情况
|
||||
|
||||
db.serverStatus().locks
|
||||
# 预期结果: 显示锁的使用统计
|
||||
|
||||
# 10. 索引使用情况分析
|
||||
# 查看索引使用统计
|
||||
db.products.aggregate([
|
||||
{ $indexStats: {} }
|
||||
])
|
||||
# 预期结果: 显示每个索引的使用次数和访问模式
|
||||
|
||||
# 识别未使用的索引
|
||||
db.products.aggregate([
|
||||
{ $indexStats: {} },
|
||||
{ $match: { "accesses.ops": 0 } }
|
||||
])
|
||||
# 预期结果: 显示从未被使用的索引(可考虑删除)
|
||||
|
||||
# 11. 查询优化最佳实践验证
|
||||
# 使用投影减少网络传输
|
||||
var startTime = new Date();
|
||||
db.products.find(
|
||||
{ category: "electronics" },
|
||||
{ name: 1, price: 1, _id: 0 } # 只返回需要的字段
|
||||
).toArray();
|
||||
var endTime = new Date();
|
||||
print("投影查询执行时间: " + (endTime - startTime) + "ms");
|
||||
|
||||
# 对比不使用投影的查询时间
|
||||
var startTime = new Date();
|
||||
db.products.find({ category: "electronics" }).toArray();
|
||||
var endTime = new Date();
|
||||
print("完整文档查询执行时间: " + (endTime - startTime) + "ms");
|
||||
# 预期结果: 使用投影的查询应该更快
|
||||
|
||||
# 12. 清理和总结
|
||||
# 关闭profiling
|
||||
db.setProfilingLevel(0)
|
||||
|
||||
# 查看profiling总结
|
||||
db.system.profile.aggregate([
|
||||
{
|
||||
$group: {
|
||||
_id: "$command.find",
|
||||
avgDuration: { $avg: "$millis" },
|
||||
maxDuration: { $max: "$millis" },
|
||||
count: { $sum: 1 }
|
||||
}
|
||||
},
|
||||
{ $sort: { avgDuration: -1 } }
|
||||
])
|
||||
# 预期结果: 显示各集合查询的平均执行时间统计
|
||||
```
|
||||
299
数据库/MongoDB_2025/MongoDB数据建模.md
Normal file
299
数据库/MongoDB_2025/MongoDB数据建模.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# 数据建模
|
||||
|
||||
与关系型数据库不同,MongoDB 灵活的文档模型为数据建模提供了更多的选择。本章节将介绍 MongoDB 数据建模的核心思想、常见模式以及如何根据应用需求选择合适的模型,以实现最佳的性能和可扩展性。
|
||||
|
||||
---
|
||||
|
||||
## 数据建模核心思想
|
||||
|
||||
### 嵌入(Embedding) vs. 引用(Referencing)
|
||||
|
||||
这是 MongoDB 数据建模中最核心的决策点。
|
||||
|
||||
- **嵌入 (Embedding / Denormalization)**
|
||||
- **描述**: 将相关的数据直接嵌入到主文档中,形成一个嵌套的文档结构。
|
||||
- **优点**:
|
||||
- **性能好**: 只需一次数据库查询即可获取所有相关数据,减少了读操作的次数。
|
||||
- **数据原子性**: 对单个文档的更新是原子操作。
|
||||
- **缺点**:
|
||||
- **文档体积大**: 可能导致文档超过 16MB 的大小限制。
|
||||
- **数据冗余**: 如果嵌入的数据在多处被使用,更新时需要修改所有包含它的文档。
|
||||
- **适用场景**: “一对一”或“一对多”关系,且“多”的一方数据不经常变动,或者总是和“一”的一方一起被查询。
|
||||
|
||||
- **引用 (Referencing / Normalization)**
|
||||
- **描述**: 将数据存储在不同的集合中,通过在文档中存储对另一个文档的引用(通常是 `_id`)来建立关系。
|
||||
- **优点**:
|
||||
- **数据一致性**: 更新数据时只需修改一处。
|
||||
- **文档体积小**: 避免了数据冗余。
|
||||
- **缺点**:
|
||||
- **查询性能较低**: 需要多次查询(或使用 `$lookup`)来获取关联数据。
|
||||
- **适用场景**: “多对多”关系,或者“一对多”关系中“多”的一方数据量巨大、经常变动或经常被独立查询。
|
||||
|
||||
### 决策依据
|
||||
|
||||
选择嵌入还是引用,主要取决于应用的**数据访问模式**:
|
||||
|
||||
- **读多写少**: 优先考虑嵌入,优化读取性能。
|
||||
- **写操作频繁**: 优先考虑引用,避免更新大量冗余数据。
|
||||
- **数据一致性要求高**: 优先考虑引用。
|
||||
- **数据关联性强,总是一起访问**: 优先考虑嵌入。
|
||||
|
||||
---
|
||||
|
||||
## 常见数据建模模式
|
||||
|
||||
MongoDB 社区总结了一些行之有效的数据建模模式,可以作为设计的参考。
|
||||
|
||||
### 属性模式 (Attribute Pattern)
|
||||
|
||||
- **问题**: 当文档有大量字段,但大部分查询只关心其中一小部分时。
|
||||
- **解决方案**: 将不常用或具有相似特征的字段分组到一个子文档中。
|
||||
- **示例**: 一个产品文档,将详细规格参数放到 `specs` 子文档中。
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": "123",
|
||||
"name": "Laptop",
|
||||
"specs": {
|
||||
"cpu": "Intel i7",
|
||||
"ram_gb": 16,
|
||||
"storage_gb": 512
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 扩展引用模式 (Extended Reference Pattern)
|
||||
|
||||
- **问题**: 在引用模型中,为了获取被引用文档的某个常用字段,需要执行额外的查询。
|
||||
- **解决方案**: 在引用文档中,除了存储 `_id` 外,还冗余存储一些经常需要一起显示的字段。
|
||||
- **示例**: 在文章(`posts`)集合中,除了存储作者的 `author_id`,还冗余存储 `author_name`。
|
||||
|
||||
```json
|
||||
// posts collection
|
||||
{
|
||||
"title": "My First Post",
|
||||
"author_id": "xyz",
|
||||
"author_name": "John Doe" // Extended Reference
|
||||
}
|
||||
```
|
||||
|
||||
### 子集模式 (Subset Pattern)
|
||||
|
||||
- **问题**: 一个文档中有一个巨大的数组(如评论、日志),导致文档过大,且大部分时间只需要访问数组的最新一部分。
|
||||
- **解决方案**: 将数组的一个子集(如最新的 10 条评论)与主文档存储在一起,完整的数组存储在另一个集合中。
|
||||
- **示例**: 产品文档中存储最新的 5 条评论,所有评论存储在 `reviews` 集合。
|
||||
|
||||
```json
|
||||
// products collection
|
||||
{
|
||||
"product_id": "abc",
|
||||
"name": "Super Widget",
|
||||
"reviews_subset": [
|
||||
{ "review_id": 1, "text": "Great!" },
|
||||
{ "review_id": 2, "text": "Awesome!" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 计算模式 (Computed Pattern)
|
||||
|
||||
- **问题**: 需要频繁计算某些值(如总数、平均值),每次读取时计算会消耗大量资源。
|
||||
- **解决方案**: 在写操作时预先计算好这些值,并将其存储在文档中。当数据更新时,同步更新计算结果。
|
||||
- **示例**: 在用户文档中存储其发布的帖子总数 `post_count`。
|
||||
|
||||
```json
|
||||
{
|
||||
"user_id": "user123",
|
||||
"name": "Jane Doe",
|
||||
"post_count": 42 // Computed value
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Schema 验证
|
||||
|
||||
虽然 MongoDB 是 schema-less 的,但在应用层面保持数据结构的一致性非常重要。从 MongoDB 3.2 开始,引入了 Schema 验证功能。
|
||||
|
||||
- **作用**: 可以在集合级别定义文档必须满足的结构规则(如字段类型、必需字段、范围等)。
|
||||
- **好处**: 确保写入数据库的数据符合预期格式,提高数据质量。
|
||||
|
||||
```javascript
|
||||
db.createCollection("students", {
|
||||
validator: {
|
||||
$jsonSchema: {
|
||||
bsonType: "object",
|
||||
required: ["name", "major", "gpa"],
|
||||
properties: {
|
||||
name: {
|
||||
bsonType: "string",
|
||||
description: "must be a string and is required"
|
||||
},
|
||||
gpa: {
|
||||
bsonType: ["double"],
|
||||
minimum: 0,
|
||||
maximum: 4.0,
|
||||
description: "must be a double in [0, 4] and is required"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实践环节
|
||||
|
||||
### 需求描述
|
||||
|
||||
为一个博客系统设计数据模型,包含以下实体:`用户 (User)`、`文章 (Post)`、`评论 (Comment)` 和 `标签 (Tag)`。
|
||||
|
||||
1. **关系分析**: 分析这些实体之间的关系(一对一、一对多、多对多)。
|
||||
2. **模型设计**: 讨论并设计至少两种不同的数据模型方案(例如,一种偏向嵌入,一种偏向引用)。
|
||||
3. **优劣对比**: 对比不同方案的优缺点,并说明它们分别适用于哪些场景。
|
||||
4. **Schema 定义**: 选择的最佳方案中的 `posts` 集合编写一个 Schema 验证规则。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```javascript
|
||||
// 1. 关系分析与模型设计
|
||||
|
||||
// 方案一:偏向嵌入式(适合读多写少的场景)
|
||||
// users 集合示例文档
|
||||
db.users.insertOne({
|
||||
"_id": ObjectId(),
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com",
|
||||
"posts": [{
|
||||
"_id": ObjectId(),
|
||||
"title": "MongoDB 入门",
|
||||
"content": "MongoDB 是一个强大的 NoSQL 数据库...",
|
||||
"created_at": ISODate("2024-01-15"),
|
||||
"tags": ["MongoDB", "Database", "NoSQL"],
|
||||
"comments": [{
|
||||
"user_id": ObjectId(),
|
||||
"username": "alice",
|
||||
"content": "写得很好!",
|
||||
"created_at": ISODate("2024-01-16")
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
// 方案二:偏向引用式(适合写多读少的场景)
|
||||
// users 集合
|
||||
db.users.insertOne({
|
||||
"_id": ObjectId(),
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com"
|
||||
});
|
||||
|
||||
// posts 集合
|
||||
db.posts.insertOne({
|
||||
"_id": ObjectId(),
|
||||
"author_id": ObjectId(), // 引用 users._id
|
||||
"author_name": "john_doe", // 扩展引用
|
||||
"title": "MongoDB 入门",
|
||||
"content": "MongoDB 是一个强大的 NoSQL 数据库...",
|
||||
"created_at": ISODate("2024-01-15"),
|
||||
"tags": ["MongoDB", "Database", "NoSQL"]
|
||||
});
|
||||
|
||||
// comments 集合
|
||||
db.comments.insertOne({
|
||||
"_id": ObjectId(),
|
||||
"post_id": ObjectId(), // 引用 posts._id
|
||||
"user_id": ObjectId(), // 引用 users._id
|
||||
"username": "alice", // 扩展引用
|
||||
"content": "写得很好!",
|
||||
"created_at": ISODate("2024-01-16")
|
||||
});
|
||||
|
||||
// 2. 性能测试
|
||||
|
||||
// 方案一:嵌入式查询(一次查询获取所有信息)
|
||||
db.users.find(
|
||||
{ "posts.tags": "MongoDB" },
|
||||
{ "posts.$": 1 }
|
||||
).explain("executionStats");
|
||||
|
||||
// 方案二:引用式查询(需要聚合或多次查询)
|
||||
db.posts.aggregate([
|
||||
{ $match: { tags: "MongoDB" } },
|
||||
{ $lookup: {
|
||||
from: "comments",
|
||||
localField: "_id",
|
||||
foreignField: "post_id",
|
||||
as: "comments"
|
||||
}}
|
||||
]).explain("executionStats");
|
||||
|
||||
// 3. Schema 验证规则
|
||||
|
||||
// 为 posts 集合创建验证规则
|
||||
db.createCollection("posts", {
|
||||
validator: {
|
||||
$jsonSchema: {
|
||||
bsonType: "object",
|
||||
required: ["author_id", "author_name", "title", "content", "created_at", "tags"],
|
||||
properties: {
|
||||
author_id: {
|
||||
bsonType: "objectId",
|
||||
description: "作者ID,必须是 ObjectId 类型且不能为空"
|
||||
},
|
||||
author_name: {
|
||||
bsonType: "string",
|
||||
description: "作者名称,必须是字符串且不能为空"
|
||||
},
|
||||
title: {
|
||||
bsonType: "string",
|
||||
minLength: 1,
|
||||
maxLength: 100,
|
||||
description: "标题长度必须在1-100字符之间"
|
||||
},
|
||||
content: {
|
||||
bsonType: "string",
|
||||
minLength: 1,
|
||||
description: "内容不能为空"
|
||||
},
|
||||
created_at: {
|
||||
bsonType: "date",
|
||||
description: "创建时间,必须是日期类型"
|
||||
},
|
||||
tags: {
|
||||
bsonType: "array",
|
||||
minItems: 1,
|
||||
uniqueItems: true,
|
||||
items: {
|
||||
bsonType: "string"
|
||||
},
|
||||
description: "标签必须是非空字符串数组,且不能重复"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
validationLevel: "strict",
|
||||
validationAction: "error"
|
||||
});
|
||||
|
||||
// 4. 验证结果
|
||||
|
||||
// 测试有效文档(应该成功)
|
||||
db.posts.insertOne({
|
||||
author_id: ObjectId(),
|
||||
author_name: "john_doe",
|
||||
title: "测试文章",
|
||||
content: "这是一篇测试文章",
|
||||
created_at: new Date(),
|
||||
tags: ["测试", "MongoDB"]
|
||||
});
|
||||
|
||||
// 测试无效文档(应该失败)
|
||||
db.posts.insertOne({
|
||||
author_name: "john_doe", // 缺少 author_id
|
||||
title: "测试文章",
|
||||
content: "这是一篇测试文章",
|
||||
created_at: new Date(),
|
||||
tags: [] // 空标签数组,违反 minItems 规则
|
||||
});
|
||||
```
|
||||
171
数据库/MongoDB_2025/MongoDB环境搭建.md
Normal file
171
数据库/MongoDB_2025/MongoDB环境搭建.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# 环境搭建
|
||||
|
||||
本章将详细介绍如何在 Rocky Linux 操作系统上安装和配置 MongoDB,以及如何管理 MongoDB 服务。正确的环境搭建是后续学习的基础。
|
||||
|
||||
---
|
||||
|
||||
## 安装部署
|
||||
|
||||
基于 Rocky Linux 9 系统上,推荐使用 `yum` 或 `dnf` 包管理器通过官方源进行安装,这样可以确保安装的是最新稳定版本,并且便于后续更新。
|
||||
|
||||
**步骤 1: 配置 MongoDB 的 YUM/DNF 仓库**
|
||||
|
||||
创建一个 `/etc/yum.repos.d/mongodb-org-7.0.repo` 文件,并添加以下内容:
|
||||
|
||||
```ini
|
||||
[mongodb-org-7.0]
|
||||
name=MongoDB Repository
|
||||
baseurl=https://mirrors.tuna.tsinghua.edu.cn/mongodb/yum/el9-7.0/
|
||||
gpgcheck=0
|
||||
enabled=1
|
||||
```
|
||||
|
||||
**步骤 2: 安装 MongoDB**
|
||||
|
||||
执行以下命令安装 MongoDB 数据库及其相关工具:
|
||||
|
||||
```shell
|
||||
# 清理缓存并安装
|
||||
yum makecache
|
||||
yum install -y mongodb-org
|
||||
```
|
||||
|
||||
这个命令会安装以下几个包:
|
||||
- `mongodb-org-server`: `mongod` 守护进程、配置文件和初始化脚本。
|
||||
- `mongodb-org-mongos`: `mongos` 守护进程。
|
||||
- `mongodb-mongosh`: MongoDB Shell (`mongosh`)。
|
||||
- `mongodb-org-tools`: MongoDB 的命令行工具,如 `mongodump`, `mongorestore`, `mongoimport`, `mongoexport` 等。
|
||||
|
||||
---
|
||||
|
||||
## 相关配置
|
||||
|
||||
MongoDB 的主配置文件位于 `/etc/mongod.conf`,默认的配置文件采用 YAML 格式。以下是一些核心配置项的说明:
|
||||
|
||||
```yaml
|
||||
# mongod.conf
|
||||
|
||||
# 存储设置
|
||||
storage:
|
||||
dbPath: /var/lib/mongo # 数据文件存放目录
|
||||
journal:
|
||||
enabled: true # 是否启用 journal 日志,建议始终开启
|
||||
|
||||
# 系统日志设置
|
||||
systemLog:
|
||||
destination: file # 日志输出到文件
|
||||
logAppend: true # 日志以追加方式写入
|
||||
path: /var/log/mongodb/mongod.log # 日志文件路径
|
||||
|
||||
# 网络设置
|
||||
net:
|
||||
port: 27017 # 监听端口
|
||||
bindIp: 127.0.0.1 # 绑定的 IP 地址,默认为本地回环地址。如需远程访问,可设置为 0.0.0.0
|
||||
|
||||
# 进程管理
|
||||
processManagement:
|
||||
timeZoneInfo: /usr/share/zoneinfo # 时区信息
|
||||
|
||||
# 安全设置 (默认禁用)
|
||||
#security:
|
||||
# authorization: enabled # 启用访问控制
|
||||
```
|
||||
|
||||
- **`storage.dbPath`**: MongoDB 数据文件的存储路径,需要确保 `mongod` 用户对此目录有读写权限。
|
||||
- **`systemLog.path`**: 日志文件路径,用于记录数据库的操作和错误信息。
|
||||
- **`net.bindIp`**: 这是非常重要的安全配置。默认 `127.0.0.1` 只允许本机连接。如果需要从其他服务器连接,应谨慎地将其设置为服务器的内网 IP 或 `0.0.0.0`(监听所有网络接口),并配合防火墙和安全组规则使用。
|
||||
|
||||
---
|
||||
|
||||
## 服务管理
|
||||
|
||||
在现代 Linux 系统中,我们使用 `systemd` 来管理 `mongod` 服务。
|
||||
|
||||
```shell
|
||||
# 启动 MongoDB 服务
|
||||
systemctl start mongod
|
||||
# 查看服务状态
|
||||
systemctl status mongod
|
||||
# 停止 MongoDB 服务
|
||||
systemctl stop mongod
|
||||
# 重启 MongoDB 服务
|
||||
systemctl restart mongod
|
||||
# 启用开机自启动
|
||||
systemctl enable mongod
|
||||
# 禁用开机自启动
|
||||
systemctl disable mongod
|
||||
```
|
||||
|
||||
## 生产配置
|
||||
|
||||
这是一个更贴近生产环境的配置文件示例,增加了安全和性能相关的配置。
|
||||
|
||||
```yaml
|
||||
# /etc/mongod.conf - Production Example
|
||||
|
||||
storage:
|
||||
dbPath: /var/lib/mongo
|
||||
journal:
|
||||
enabled: true
|
||||
engine: wiredTiger
|
||||
wiredTiger:
|
||||
engineConfig:
|
||||
cacheSizeGB: 1 # 根据服务器内存调整,建议为物理内存的 50% - 60%
|
||||
|
||||
systemLog:
|
||||
destination: file
|
||||
logAppend: true
|
||||
path: /var/log/mongodb/mongod.log
|
||||
|
||||
net:
|
||||
port: 27017
|
||||
bindIp: 127.0.0.1,192.168.1.100 # 绑定本地和内网 IP
|
||||
maxIncomingConnections: 1000 # 最大连接数
|
||||
|
||||
processManagement:
|
||||
fork: true # 后台运行
|
||||
pidFilePath: /var/run/mongodb/mongod.pid
|
||||
timeZoneInfo: /usr/share/zoneinfo
|
||||
|
||||
security:
|
||||
authorization: enabled # 开启认证
|
||||
|
||||
replication:
|
||||
replSetName: rs0 # 副本集名称
|
||||
```
|
||||
|
||||
## 实践操作
|
||||
|
||||
### 需求描述
|
||||
|
||||
1. 使用 `root` 用户安装并正常运行
|
||||
2. 更改数据目录 `dbPath` 为 `/data/mongodb`
|
||||
3. 更改监听地址 `bindIp` 为 `0.0.0.0`
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```shell
|
||||
# 关闭防火墙和 SELINUX
|
||||
|
||||
# 安装 Mongodb 并正常启动
|
||||
tee /etc/yum.repos.d/mongodb-org-7.0.repo << EOF
|
||||
[mongodb-org-7.0]
|
||||
name=MongoDB Repository
|
||||
baseurl=https://mirrors.tuna.tsinghua.edu.cn/mongodb/yum/el9-7.0/
|
||||
gpgcheck=0
|
||||
enabled=1
|
||||
EOF
|
||||
yum makecache
|
||||
yum install -y mongodb-org
|
||||
mkdir -p /data/mongodb
|
||||
sed -i 's|dbPath: /var/lib/mongo|dbPath: /data/mongodb|' /etc/mongod.conf
|
||||
sed -i 's|bindIp: 127.0.0.1|bindIp: 0.0.0.0|' /etc/mongod.conf
|
||||
cp /lib/systemd/system/mongod.service /etc/systemd/system/mongod.service
|
||||
sed -i 's/User=mongod/User=root/' /etc/systemd/system/mongod.service
|
||||
sed -i 's/Group=mongod/Group=root/' /etc/systemd/system/mongod.service
|
||||
systemctl start mongod && systemctl enable mongod
|
||||
|
||||
# 测试验证
|
||||
mongod --version
|
||||
```
|
||||
|
||||
267
数据库/MongoDB_2025/MongoDB索引优化.md
Normal file
267
数据库/MongoDB_2025/MongoDB索引优化.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# 索引优化
|
||||
|
||||
索引是提升 MongoDB 查询性能的关键。本章节将深入探讨索引的类型、创建策略、如何分析查询性能以及索引的维护方法,帮助大家构建高性能的数据库应用。
|
||||
|
||||
---
|
||||
|
||||
## 索引基础
|
||||
|
||||
### 什么是索引?
|
||||
|
||||
索引是一种特殊的数据结构,它以易于遍历的形式存储了集合中一小部分的数据。通过索引,MongoDB 可以直接定位到符合查询条件的文档,而无需扫描整个集合,从而极大地提高了查询速度。
|
||||
|
||||
### 索引的类型
|
||||
|
||||
MongoDB 支持多种类型的索引,以适应不同的查询需求。
|
||||
|
||||
1. **单字段索引 (Single Field Index)**
|
||||
- 最基础的索引类型,针对单个字段创建。
|
||||
- 示例: `db.inventory.createIndex({ price: 1 })` (按价格升序)
|
||||
|
||||
2. **复合索引 (Compound Index)**
|
||||
- 在多个字段上创建的索引。字段的顺序非常重要,决定了索引的查询效率。
|
||||
- 示例: `db.inventory.createIndex({ price: 1, qty: -1 })` (先按价格升序,再按数量降序)
|
||||
|
||||
3. **多键索引 (Multikey Index)**
|
||||
- 为数组字段创建的索引。MongoDB 会为数组中的每个元素创建一条索引项。
|
||||
- 示例: `db.inventory.createIndex({ tags: 1 })` (为 `tags` 数组中的每个标签创建索引)
|
||||
|
||||
4. **文本索引 (Text Index)**
|
||||
- 用于支持对字符串内容的文本搜索查询。
|
||||
- 示例: `db.inventory.createIndex({ description: "text" })`
|
||||
|
||||
5. **地理空间索引 (Geospatial Index)**
|
||||
- 用于高效地查询地理空间坐标数据。
|
||||
- 类型: `2dsphere` (用于球面几何) 和 `2d` (用于平面几何)。
|
||||
- 示例: `db.places.createIndex({ location: "2dsphere" })`
|
||||
|
||||
6. **哈希索引 (Hashed Index)**
|
||||
- 计算字段值的哈希值并对哈希值建立索引,主要用于哈希分片。
|
||||
- 示例: `db.inventory.createIndex({ status: "hashed" })`
|
||||
|
||||
---
|
||||
|
||||
## 索引策略与创建
|
||||
|
||||
### 创建索引
|
||||
|
||||
使用 `createIndex()` 方法在集合上创建索引。
|
||||
|
||||
```javascript
|
||||
// 在 students 集合的 email 字段上创建一个唯一的升序索引
|
||||
db.students.createIndex({ student_id: 1 }, { unique: true })
|
||||
|
||||
// 创建一个后台索引,避免阻塞数据库操作
|
||||
db.students.createIndex({ name: 1 }, { background: true })
|
||||
```
|
||||
|
||||
### 索引属性
|
||||
|
||||
- **`unique`**: 强制索引字段的值唯一,拒绝包含重复值的文档插入或更新。
|
||||
- **`sparse`**: 稀疏索引,只为包含索引字段的文档创建索引项,节省空间。
|
||||
- **`background`**: 在后台创建索引,允许在创建过程中进行读写操作(但会稍慢)。
|
||||
- **`expireAfterSeconds`**: TTL (Time-To-Live) 索引,自动从集合中删除超过指定时间的文档。
|
||||
|
||||
### 复合索引的字段顺序
|
||||
|
||||
复合索引的字段顺序遵循 **ESR (Equality, Sort, Range)** 法则:
|
||||
|
||||
1. **等值查询 (Equality)** 字段应放在最前面。
|
||||
2. **排序 (Sort)** 字段其次。
|
||||
3. **范围查询 (Range)** 字段(如 `$gt`, `$lt`)应放在最后。
|
||||
|
||||
正确的字段顺序可以使一个索引服务于多种查询,提高索引的复用率。
|
||||
|
||||
**案例分析**:
|
||||
|
||||
假设我们有一个 `products` 集合,需要频繁执行一个查询:查找特定 `category` 的商品,按 `brand` 排序,并筛选出价格 `price` 大于某个值的商品。
|
||||
|
||||
**查询语句**:
|
||||
```javascript
|
||||
db.products.find(
|
||||
{
|
||||
category: "electronics", // 等值查询 (Equality)
|
||||
price: { $gt: 500 } // 范围查询 (Range)
|
||||
}
|
||||
).sort({ brand: 1 }) // 排序 (Sort)
|
||||
```
|
||||
|
||||
**根据 ESR 法则创建索引**:
|
||||
|
||||
遵循 ESR 法则,我们应该将等值查询字段 `category` 放在最前面,然后是排序字段 `brand`,最后是范围查询字段 `price`。
|
||||
|
||||
**最佳索引**:
|
||||
```javascript
|
||||
db.products.createIndex({ category: 1, brand: 1, price: 1 })
|
||||
```
|
||||
|
||||
**为什么这个顺序是最高效的?**
|
||||
|
||||
1. **等值查询优先 (`category`)**: MongoDB 可以立即通过索引定位到所有 `category` 为 `"electronics"` 的文档,快速缩小查询范围。
|
||||
2. **排序字段次之 (`brand`)**: 在已筛选出的 `"electronics"` 索引条目中,数据已经按 `brand` 排好序。因此,MongoDB 无需在内存中执行额外的排序操作 (`in-memory sort`),极大地提升了性能。
|
||||
3. **范围查询最后 (`price`)**: 在已经按 `brand` 排序的索引条目上,MongoDB 可以顺序扫描,高效地过滤出 `price` 大于 500 的条目。
|
||||
|
||||
如果索引顺序不当,例如 `{ price: 1, brand: 1, category: 1 }`,MongoDB 将无法有效利用索引进行排序,可能导致性能下降。
|
||||
|
||||
---
|
||||
|
||||
## 查询性能分析
|
||||
|
||||
### `explain()` 方法
|
||||
|
||||
`explain()` 是分析查询性能的利器。它可以显示查询的执行计划,包括是否使用了索引、扫描了多少文档等信息。
|
||||
|
||||
```javascript
|
||||
db.students.find({ age: { $gt: 21 } }).explain("executionStats")
|
||||
```
|
||||
|
||||
### 分析 `explain()` 结果
|
||||
|
||||
关注 `executionStats` 中的关键指标:
|
||||
|
||||
- **`executionSuccess`**: 查询是否成功执行。
|
||||
- **`nReturned`**: 查询返回的文档数量。
|
||||
- **`totalKeysExamined`**: 扫描的索引键数量。
|
||||
- **`totalDocsExamined`**: 扫描的文档数量。
|
||||
- **`executionTimeMillis`**: 查询执行时间(毫秒)。
|
||||
- **`winningPlan.stage`**: 查询计划的阶段。
|
||||
- `COLLSCAN`: 全集合扫描,性能最低。
|
||||
- `IXSCAN`: 索引扫描,性能较高。
|
||||
- `FETCH`: 根据索引指针去获取文档。
|
||||
|
||||
**理想情况**: `totalKeysExamined` 和 `totalDocsExamined` 应该尽可能接近 `nReturned`。
|
||||
|
||||
### 覆盖查询 (Covered Query)
|
||||
|
||||
当查询所需的所有字段都包含在索引中时,MongoDB 可以直接从索引返回结果,而无需访问文档,这称为覆盖查询。覆盖查询性能极高。
|
||||
|
||||
**条件**:
|
||||
1. 查询的所有字段都是索引的一部分。
|
||||
2. 查询返回的所有字段都在同一个索引中。
|
||||
3. 查询的字段中不包含 `_id` 字段,或者 `_id` 字段是索引的一部分(默认情况下 `_id` 会被返回,除非显式排除)。
|
||||
|
||||
**案例分析**:
|
||||
|
||||
假设我们有一个 `students` 集合,并且我们经常需要通过 `student_id` 查找学生的姓名 `name`。
|
||||
|
||||
1. **创建复合索引**:
|
||||
为了优化这个查询,我们可以在 `student_id` 和 `name` 字段上创建一个复合索引。
|
||||
|
||||
```javascript
|
||||
db.students.createIndex({ student_id: 1, name: 1 })
|
||||
```
|
||||
|
||||
2. **执行覆盖查询**:
|
||||
现在,我们执行一个只查询 `student_id` 并只返回 `name` 字段的查询。我们使用投影 (projection) 来显式排除 `_id` 字段,并只包含 `name` 字段。
|
||||
|
||||
```javascript
|
||||
db.students.find({ student_id: 'S1001' }, { name: 1, _id: 0 })
|
||||
```
|
||||
|
||||
3. **性能验证**:
|
||||
使用 `explain()` 方法来查看查询的执行计划。
|
||||
|
||||
```javascript
|
||||
db.students.find({ student_id: 'S1001' }, { name: 1, _id: 0 }).explain('executionStats')
|
||||
```
|
||||
|
||||
在 `executionStats` 的输出中,我们会发现:
|
||||
- `totalDocsExamined` 的值为 `0`。这表明 MongoDB 没有扫描任何文档。
|
||||
- `totalKeysExamined` 的值大于 `0`,说明扫描了索引。
|
||||
- `winningPlan.stage` 会显示为 `IXSCAN`,并且没有 `FETCH` 阶段。
|
||||
|
||||
这个结果证明了该查询是一个覆盖查询。MongoDB 仅通过访问索引就获取了所有需要的数据,完全避免了读取文档的磁盘 I/O 操作,从而实现了极高的查询性能。
|
||||
|
||||
---
|
||||
|
||||
## 索引维护
|
||||
|
||||
### 查看索引
|
||||
|
||||
使用 `getIndexes()` 方法查看集合上的所有索引。
|
||||
|
||||
```javascript
|
||||
db.students.getIndexes()
|
||||
```
|
||||
|
||||
### 删除索引
|
||||
|
||||
使用 `dropIndex()` 方法删除指定的索引。
|
||||
|
||||
```javascript
|
||||
// 按名称删除索引
|
||||
db.students.dropIndex("email_1")
|
||||
|
||||
// 按键模式删除索引
|
||||
db.students.dropIndex({ last_login: -1 })
|
||||
```
|
||||
|
||||
### 索引大小和使用情况
|
||||
|
||||
使用 `$indexStats` 聚合阶段可以查看索引的大小和使用情况(自上次重启以来的操作次数)。
|
||||
|
||||
```javascript
|
||||
db.students.aggregate([{ $indexStats: {} }])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实践操作
|
||||
|
||||
在本节中,我们将使用 `products_for_indexing` 集合进行实践,数据已在 `data.js` 中定义。
|
||||
|
||||
### 需求描述
|
||||
|
||||
假设我们有一个电商平台的 `products02` 集合,需要支持以下查询场景:
|
||||
1. 频繁按商品类别 (`category`) 查找商品,并按价格 (`price`) 排序。
|
||||
2. 需要快速获取特定类别和品牌的商品信息,且只关心品牌和价格。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```javascript
|
||||
// 准备工作:确保 products_for_indexing 集合存在且包含数据
|
||||
// (数据已在 data.js 中定义,请先加载)
|
||||
|
||||
// 1. 创建复合索引以优化排序查询
|
||||
// 需求:按类别查找并按价格排序
|
||||
db.products_for_indexing.createIndex({ category: 1, price: 1 });
|
||||
|
||||
// 2. 使用 explain() 分析查询性能
|
||||
|
||||
// -- 无索引查询 (模拟,假设索引未创建) --
|
||||
// db.products_for_indexing.find({ category: 'Electronics' }).sort({ price: -1 }).explain('executionStats');
|
||||
// 结果会显示 COLLSCAN (全表扫描),效率低
|
||||
|
||||
// -- 有索引查询 --
|
||||
db.products_for_indexing.find({ category: 'Electronics' }).sort({ price: -1 }).explain('executionStats');
|
||||
// **结果验证**:
|
||||
// winningPlan.stage 应为 IXSCAN (索引扫描),表明使用了索引。
|
||||
// totalDocsExamined 数量远小于集合总数,性能高。
|
||||
|
||||
// 3. 构造并验证覆盖查询
|
||||
// 需求:只查询电子产品的品牌和价格
|
||||
|
||||
// -- 创建一个能覆盖查询的索引 --
|
||||
db.products_for_indexing.createIndex({ category: 1, brand: 1, price: 1 });
|
||||
|
||||
// -- 执行覆盖查询 --
|
||||
db.products_for_indexing.find(
|
||||
{ category: 'Electronics', brand: 'Sony' },
|
||||
{ brand: 1, price: 1, _id: 0 }
|
||||
).explain('executionStats');
|
||||
// **结果验证**:
|
||||
// totalDocsExamined 应为 0,表明没有读取文档。
|
||||
// winningPlan.stage 为 IXSCAN,且没有 FETCH 阶段,证明是高效的覆盖查询。
|
||||
|
||||
// 4. 索引维护
|
||||
|
||||
// -- 查看当前集合上的所有索引 --
|
||||
db.products_for_indexing.getIndexes();
|
||||
|
||||
// -- 删除一个不再需要的索引 --
|
||||
// 假设 { category: 1, price: 1 } 这个索引不再需要
|
||||
db.products_for_indexing.dropIndex('category_1_price_1');
|
||||
|
||||
// -- 再次查看索引,确认已删除 --
|
||||
db.products_for_indexing.getIndexes();
|
||||
```
|
||||
267
数据库/MongoDB_2025/MongoDB聚合框架.md
Normal file
267
数据库/MongoDB_2025/MongoDB聚合框架.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# 聚合框架
|
||||
|
||||
聚合框架是 MongoDB 提供的一个强大的数据处理工具,它允许对集合中的数据进行一系列的转换和计算,最终得到聚合后的结果。本章节将深入探讨聚合管道、常用的聚合阶段以及如何利用聚合框架进行复杂的数据分析。
|
||||
|
||||
---
|
||||
|
||||
## 聚合管道(Aggregation Pipeline)
|
||||
|
||||
聚合操作的核心是聚合管道。管道由一个或多个**阶段 (Stage)** 组成,每个阶段都会对输入的文档流进行处理,并将结果传递给下一个阶段。
|
||||
|
||||
### 聚合管道的语法
|
||||
|
||||
聚合操作使用 `aggregate()` 方法,其参数是一个包含所有阶段的数组。
|
||||
|
||||
```javascript
|
||||
db.collection.aggregate([
|
||||
{ <stage1> },
|
||||
{ <stage2> },
|
||||
...
|
||||
])
|
||||
```
|
||||
|
||||
### 聚合管道的优势
|
||||
|
||||
- **功能强大**: 支持复杂的数据转换、分组、计算和重塑。
|
||||
- **性能高效**: 许多操作在数据库服务端以原生代码执行,减少了数据在网络中的传输。
|
||||
- **灵活性高**: 可以通过组合不同的阶段来满足各种复杂的数据处理需求。
|
||||
|
||||
---
|
||||
|
||||
## 常用聚合阶段
|
||||
|
||||
以下是一些最常用的聚合阶段,通过组合它们可以实现强大的数据处理能力。
|
||||
|
||||
### `$match`
|
||||
|
||||
- **功能**: 过滤文档,只将满足条件的文档传递给下一个阶段。类似于 `find()` 方法的查询条件。
|
||||
- **建议**: 尽可能将 `$match` 放在管道的开头,以尽早减少需要处理的文档数量,提高效率。
|
||||
- **示例**: 筛选出状态为 "A" 的订单。
|
||||
|
||||
```javascript
|
||||
{ $match: { status: "A" } }
|
||||
```
|
||||
|
||||
### `$project`
|
||||
|
||||
- **功能**: 重塑文档流。可以包含、排除、重命名字段,或者通过表达式计算新字段。
|
||||
- **示例**: 只保留 `_id`、`name` 字段,并创建一个新的 `bonus` 字段。
|
||||
|
||||
```javascript
|
||||
{ $project: { name: 1, bonus: { $multiply: ["$salary", 0.1] } } }
|
||||
```
|
||||
|
||||
### `$group`
|
||||
|
||||
- **功能**: 按指定的 `_id` 表达式对文档进行分组,并对每个分组应用累加器表达式进行计算。
|
||||
- **核心**: `_id` 字段定义了分组的键。
|
||||
- **示例**: 按 `cust_id` 分组,并计算每个客户的订单总金额。
|
||||
|
||||
```javascript
|
||||
{
|
||||
$group: {
|
||||
_id: "$cust_id",
|
||||
totalAmount: { $sum: "$amount" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `$sort`
|
||||
|
||||
- **功能**: 对文档流进行排序,与 `find()` 中的 `sort()` 类似。
|
||||
- **建议**: 如果需要排序,尽量在管道的早期阶段进行,特别是当排序字段有索引时。
|
||||
- **示例**: 按 `totalAmount` 降序排序。
|
||||
|
||||
```javascript
|
||||
{ $sort: { totalAmount: -1 } }
|
||||
```
|
||||
|
||||
### `$limit` 和 `$skip`
|
||||
|
||||
- **功能**: 分别用于限制和跳过文档数量,实现分页。
|
||||
- **示例**: 返回排序后的前 5 个结果。
|
||||
|
||||
```javascript
|
||||
{ $limit: 5 }
|
||||
```
|
||||
|
||||
### `$unwind`
|
||||
|
||||
- **功能**: 将文档中的数组字段拆分成多个文档,数组中的每个元素都会生成一个新的文档(与其他字段组合)。
|
||||
- **示例**: 将 `tags` 数组拆分。
|
||||
|
||||
```javascript
|
||||
// 输入: { _id: 1, item: "A", tags: ["x", "y"] }
|
||||
{ $unwind: "$tags" }
|
||||
// 输出:
|
||||
// { _id: 1, item: "A", tags: "x" }
|
||||
// { _id: 1, item: "A", tags: "y" }
|
||||
```
|
||||
|
||||
### `$lookup`
|
||||
|
||||
- **功能**: 实现左外连接(Left Outer Join),将当前集合与另一个集合的文档进行关联。
|
||||
- **示例**: 将 `orders` 集合与 `inventory` 集合关联起来。
|
||||
|
||||
```javascript
|
||||
{
|
||||
$lookup: {
|
||||
from: "inventory",
|
||||
localField: "item",
|
||||
foreignField: "sku",
|
||||
as: "inventory_docs"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 聚合累加器表达式
|
||||
|
||||
累加器主要在 `$group` 阶段使用,用于对分组后的数据进行计算。
|
||||
|
||||
| 累加器 | 描述 |
|
||||
| :--- | :--- |
|
||||
| `$sum` | 计算总和 |
|
||||
| `$avg` | 计算平均值 |
|
||||
| `$min` | 获取最小值 |
|
||||
| `$max` | 获取最大值 |
|
||||
| `$first` | 获取每个分组的第一条文档的字段值 |
|
||||
| `$last` | 获取每个分组的最后一条文档的字段值 |
|
||||
| `$push` | 将字段值添加到一个数组中 |
|
||||
| `$addToSet` | 将唯一的字段值添加到一个数组中 |
|
||||
|
||||
---
|
||||
|
||||
## 聚合管道优化
|
||||
|
||||
- **尽早过滤**: 将 `$match` 阶段放在管道的最前面。
|
||||
- **尽早投影**: 使用 `$project` 移除不需要的字段,减少后续阶段的数据处理量。
|
||||
- **利用索引**: 如果 `$match` 或 `$sort` 阶段的字段有索引,MongoDB 可以利用它来优化性能。
|
||||
- **避免在分片键上 `$unwind`**: 这可能会导致性能问题。
|
||||
|
||||
---
|
||||
|
||||
## 实践环节
|
||||
|
||||
### 需求描述
|
||||
|
||||
假设有一个 `sales` 集合,包含 `product`, `quantity`, `price`, `date` 字段。
|
||||
|
||||
1. **计算总销售额**: 计算每个产品的总销售额(`quantity * price`)。
|
||||
2. **查找最畅销产品**: 按销售额降序排列,找出销售额最高的前 5 个产品。
|
||||
3. **按月统计销售**: 按月份对所有销售数据进行分组,并计算每月的总销售额和平均订单金额。
|
||||
4. **关联查询**: 假设还有一个 `products_for_aggregation` 集合(包含 `name`, `category`),使用 `$lookup` 将销售数据与产品类别关联起来,并按类别统计销售额。
|
||||
5.
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```javascript
|
||||
// 准备工作:确保已在 mongo shell 中加载 data.js 文件
|
||||
|
||||
// 1. 计算每个产品的总销售额
|
||||
db.sales.aggregate([
|
||||
{
|
||||
$group: {
|
||||
_id: "$product",
|
||||
totalRevenue: { $sum: { $multiply: ["$quantity", "$price"] } }
|
||||
}
|
||||
}
|
||||
])
|
||||
/*
|
||||
预期结果:
|
||||
[
|
||||
{ _id: 'Mouse', totalRevenue: 125 },
|
||||
{ _id: 'Keyboard', totalRevenue: 75 },
|
||||
{ _id: 'Monitor', totalRevenue: 300 },
|
||||
{ _id: 'Webcam', totalRevenue: 50 },
|
||||
{ _id: 'Laptop', totalRevenue: 4700 }
|
||||
]
|
||||
*/
|
||||
|
||||
// 2. 查找最畅销的前 5 个产品(按销售额)
|
||||
db.sales.aggregate([
|
||||
{
|
||||
$group: {
|
||||
_id: "$product",
|
||||
totalRevenue: { $sum: { $multiply: ["$quantity", "$price"] } }
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { totalRevenue: -1 }
|
||||
},
|
||||
{
|
||||
$limit: 5
|
||||
}
|
||||
])
|
||||
/*
|
||||
预期结果:
|
||||
[
|
||||
{ _id: 'Laptop', totalRevenue: 4700 },
|
||||
{ _id: 'Monitor', totalRevenue: 300 },
|
||||
{ _id: 'Mouse', totalRevenue: 125 },
|
||||
{ _id: 'Keyboard', totalRevenue: 75 },
|
||||
{ _id: 'Webcam', totalRevenue: 50 }
|
||||
]
|
||||
*/
|
||||
|
||||
// 3. 按月统计销售额
|
||||
db.sales.aggregate([
|
||||
{
|
||||
$group: {
|
||||
_id: { $month: "$date" }, // 按月份分组
|
||||
totalMonthlyRevenue: { $sum: { $multiply: ["$quantity", "$price"] } },
|
||||
averageOrderValue: { $avg: { $multiply: ["$quantity", "$price"] } }
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { _id: 1 } // 按月份升序排序
|
||||
}
|
||||
])
|
||||
/*
|
||||
预期结果:
|
||||
[
|
||||
{ _id: 1, totalMonthlyRevenue: 1325, averageOrderValue: 441.666... },
|
||||
{ _id: 2, totalMonthlyRevenue: 1675, averageOrderValue: 558.333... },
|
||||
{ _id: 3, totalMonthlyRevenue: 2250, averageOrderValue: 1125 }
|
||||
]
|
||||
*/
|
||||
|
||||
// 4. 关联查询:按产品类别统计销售额
|
||||
db.sales.aggregate([
|
||||
// 阶段一: 计算每笔销售的销售额
|
||||
{
|
||||
$project: {
|
||||
product: 1,
|
||||
revenue: { $multiply: ["$quantity", "$price"] }
|
||||
}
|
||||
},
|
||||
// 阶段二: 关联 products 集合获取类别信息
|
||||
{
|
||||
$lookup: {
|
||||
from: "products_for_aggregation",
|
||||
localField: "product",
|
||||
foreignField: "name",
|
||||
as: "productDetails"
|
||||
}
|
||||
},
|
||||
// 阶段三: 展开 productDetails 数组
|
||||
{
|
||||
$unwind: "$productDetails"
|
||||
},
|
||||
// 阶段四: 按类别分组统计总销售额
|
||||
{
|
||||
$group: {
|
||||
_id: "$productDetails.category",
|
||||
totalCategoryRevenue: { $sum: "$revenue" }
|
||||
}
|
||||
}
|
||||
])
|
||||
/*
|
||||
预期结果:
|
||||
[
|
||||
{ _id: 'Accessories', totalCategoryRevenue: 50 },
|
||||
{ _id: 'Electronics', totalCategoryRevenue: 5200 }
|
||||
]
|
||||
*/
|
||||
```
|
||||
167
数据库/MongoDB_2025/MongoDB进阶查询.md
Normal file
167
数据库/MongoDB_2025/MongoDB进阶查询.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# 查询进阶
|
||||
|
||||
本章节将带大家深入了解 MongoDB 强大的查询功能,包括使用查询操作符、投影、排序、分页以及正则表达式查询,可以更精确、更高效地从数据库中检索数据。
|
||||
|
||||
---
|
||||
|
||||
## 查询操作符
|
||||
|
||||
查询操作符是 MongoDB 查询语言的核心,它们可以帮助大家构建更复杂的查询条件。
|
||||
|
||||
### 比较操作符
|
||||
|
||||
| 操作符 | 描述 | 示例 |
|
||||
| :--- | :--- | :--- |
|
||||
| `$eq` | 等于 (Equal) | `db.inventory.find({ qty: { $eq: 20 } })` |
|
||||
| `$ne` | 不等于 (Not Equal) | `db.inventory.find({ qty: { $ne: 20 } })` |
|
||||
| `$gt` | 大于 (Greater Than) | `db.inventory.find({ qty: { $gt: 20 } })` |
|
||||
| `$gte` | 大于等于 (Greater Than or Equal) | `db.inventory.find({ qty: { $gte: 20 } })` |
|
||||
| `$lt` | 小于 (Less Than) | `db.inventory.find({ qty: { $lt: 20 } })` |
|
||||
| `$lte` | 小于等于 (Less Than or Equal) | `db.inventory.find({ qty: { $lte: 20 } })` |
|
||||
| `$in` | 在指定数组内 (In) | `db.inventory.find({ qty: { $in: [5, 15] } })` |
|
||||
| `$nin` | 不在指定数组内 (Not In) | `db.inventory.find({ qty: { $nin: [5, 15] } })` |
|
||||
|
||||
### 逻辑操作符
|
||||
|
||||
| 操作符 | 描述 | 示例 |
|
||||
| :--- | :--- | :--- |
|
||||
| `$and` | 逻辑与,连接多个查询条件 | `db.inventory.find({ $and: [{ qty: { $lt: 20 } }, { price: { $gt: 10 } }] })` |
|
||||
| `$or` | 逻辑或,满足任一查询条件 | `db.inventory.find({ $or: [{ qty: { $lt: 20 } }, { price: { $gt: 50 } }] })` |
|
||||
| `$nor` | 逻辑非或,所有条件都不满足 | `db.inventory.find({ $nor: [{ price: 1.99 }, { sale: true }] })` |
|
||||
| `$not` | 逻辑非,对指定条件取反 | `db.inventory.find({ price: { $not: { $gt: 1.99 } } })` |
|
||||
|
||||
### 元素操作符
|
||||
|
||||
| 操作符 | 描述 | 示例 |
|
||||
| :--- | :--- | :--- |
|
||||
| `$exists` | 判断字段是否存在 | `db.inventory.find({ qty: { $exists: true } })` |
|
||||
| `$type` | 判断字段的数据类型 | `db.inventory.find({ qty: { $type: "number" } })` |
|
||||
|
||||
### 数组操作符
|
||||
|
||||
| 操作符 | 描述 | 示例 |
|
||||
| :--- | :--- | :--- |
|
||||
| `$all` | 匹配包含所有指定元素的数组 | `db.inventory.find({ tags: { $all: ["appliance", "school"] } })` |
|
||||
| `$elemMatch` | 数组中至少有一个元素满足所有指定条件 | `db.inventory.find({ results: { $elemMatch: { product: "xyz", score: { $gte: 8 } } } })` |
|
||||
| `$size` | 匹配指定大小的数组 | `db.inventory.find({ tags: { $size: 3 } })` |
|
||||
|
||||
---
|
||||
|
||||
## 投影(Projection)
|
||||
|
||||
投影用于限制查询结果中返回的字段,可以减少网络传输的数据量,并保护敏感字段。
|
||||
|
||||
- **包含字段**: 在 `find()` 方法的第二个参数中,将需要返回的字段设置为 `1`。
|
||||
|
||||
```javascript
|
||||
// 只返回 name 和 price 字段,_id 默认返回
|
||||
db.products.find({}, { name: 1, price: 1 })
|
||||
```
|
||||
|
||||
- **排除字段**: 将不需要返回的字段设置为 `0`。
|
||||
|
||||
```javascript
|
||||
// 返回除 description 之外的所有字段
|
||||
db.products.find({}, { description: 0 })
|
||||
```
|
||||
|
||||
- **排除 `_id` 字段**:
|
||||
|
||||
```javascript
|
||||
db.products.find({}, { name: 1, price: 1, _id: 0 })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 排序(Sorting)
|
||||
|
||||
使用 `sort()` 方法对查询结果进行排序。
|
||||
|
||||
- **升序 (Ascending)**: 将字段值设置为 `1`。
|
||||
- **降序 (Descending)**: 将字段值设置为 `-1`。
|
||||
|
||||
```javascript
|
||||
// 按价格升序排序
|
||||
db.products.find().sort({ price: 1 })
|
||||
|
||||
// 按库存降序、名称升序排序
|
||||
db.products.find().sort({ stock: -1, name: 1 })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 分页(Pagination)
|
||||
|
||||
通过组合使用 `limit()` 和 `skip()` 方法,可以实现对查询结果的分页。
|
||||
|
||||
- **`limit()`**: 限制返回的文档数量。
|
||||
- **`skip()`**: 跳过指定数量的文档。
|
||||
|
||||
```javascript
|
||||
// 获取第 2 页数据,每页 10 条 (跳过前 10 条,返回 10 条)
|
||||
db.products.find().skip(10).limit(10)
|
||||
```
|
||||
|
||||
**注意**: 对于大数据量的集合,使用 `skip()` 进行深度分页可能会有性能问题。在这种情况下,建议使用基于范围的查询(如使用 `_id` 或时间戳)。
|
||||
|
||||
---
|
||||
|
||||
## 正则表达式查询
|
||||
|
||||
MongoDB 支持使用 PCRE (Perl Compatible Regular Expression) 语法的正则表达式进行字符串匹配。
|
||||
|
||||
```javascript
|
||||
// 查询 name 字段以 'A' 开头的文档 (不区分大小写)
|
||||
db.products.find({ name: { $regex: '^A', $options: 'i' } })
|
||||
|
||||
// 查询 name 字段包含 'pro' 的文档
|
||||
db.products.find({ name: /pro/ })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实践操作
|
||||
|
||||
假设有一个 `students` 集合,包含以下字段:`name`, `age`, `major`, `gpa`, `courses` (数组)。
|
||||
|
||||
### 需求描述
|
||||
|
||||
1. **查询练习**:
|
||||
- 查询所有主修 'Computer Science' 且 GPA 大于 3.5 的学生。
|
||||
- 查询年龄在 20 到 22 岁之间(含)的学生。
|
||||
- 查询选修了 'Database Systems' 和 'Data Structures' 两门课的学生。
|
||||
|
||||
2. **投影练习**:
|
||||
- 只返回学生的姓名和专业。
|
||||
|
||||
3. **排序和分页练习**:
|
||||
- 按 GPA 降序显示前 10 名学生。
|
||||
|
||||
4. **正则表达式练习**:
|
||||
- 查询所有姓 'Li' 的学生。
|
||||
|
||||
### 实践细节和结果验证
|
||||
|
||||
```javascript
|
||||
// 1. 查询练习
|
||||
// 查询所有主修 'Computer Science' 且 GPA 大于 3.5 的学生
|
||||
db.students.find({ major: 'Computer Science', gpa: { $gt: 3.5 } });
|
||||
|
||||
// 查询年龄在 20 到 22 岁之间(含)的学生
|
||||
db.students.find({ age: { $gte: 20, $lte: 22 } });
|
||||
|
||||
// 查询选修了 'Database Systems' 和 'Data Structures' 两门课的学生
|
||||
db.students.find({ courses: { $all: ['Database Systems', 'Data Structures'] } });
|
||||
|
||||
// 2. 投影练习
|
||||
// 只返回学生的姓名和专业
|
||||
db.students.find({}, { name: 1, major: 1, _id: 0 });
|
||||
|
||||
// 3. 排序和分页练习
|
||||
// 按 GPA 降序显示前 10 名学生
|
||||
db.students.find().sort({ gpa: -1 }).limit(10);
|
||||
|
||||
// 4. 正则表达式练习
|
||||
// 查询所有姓 'Li' 的学生
|
||||
db.students.find({ name: /^Li/ });
|
||||
```
|
||||
145
数据库/MongoDB_2025/data.js
Normal file
145
数据库/MongoDB_2025/data.js
Normal file
@@ -0,0 +1,145 @@
|
||||
// This file contains sample data for all MongoDB practice sessions.
|
||||
// Load this file into your mongo shell using: load('/path/to/this/file/data.js')
|
||||
|
||||
// --- Data for: MongoDB基础操作.md ---
|
||||
db.inventory.drop();
|
||||
db.inventory.insertMany([
|
||||
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A", tags: ["blank", "red"], price: 15, sale: false },
|
||||
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A", tags: ["school", "office"], price: 20, sale: true },
|
||||
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D", tags: ["office", "storage"], price: 10, sale: false },
|
||||
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D", tags: ["school", "organization"], price: 12, sale: true },
|
||||
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A", tags: ["appliance", "school", "storage"], price: 5, sale: false },
|
||||
{ item: "mousepad", qty: 20, size: { h: 19, w: 22, uom: "cm" }, status: "P", price: 25, sale: true, results: [{ product: "xyz", score: 10 }, { product: "abc", score: 8 }] },
|
||||
{ item: "keyboard", qty: 5, price: 55, sale: false, results: [{ product: "xyz", score: 7 }, { product: "abc", score: 6 }] },
|
||||
{ item: "stapler", qty: 15, price: 1.99, sale: true, results: [{ product: "xyz", score: 9 }, { product: "abc", score: 4 }] }
|
||||
]);
|
||||
|
||||
db.books.drop();
|
||||
db.books.insertMany([
|
||||
{
|
||||
"title": "Dune",
|
||||
"author": "Frank Herbert",
|
||||
"published_year": 1965,
|
||||
"genres": ["Science Fiction", "Fantasy"],
|
||||
"stock": 8
|
||||
},
|
||||
{
|
||||
"title": "Foundation",
|
||||
"author": "Isaac Asimov",
|
||||
"published_year": 1951,
|
||||
"genres": ["Science Fiction"],
|
||||
"stock": 15
|
||||
},
|
||||
{
|
||||
"title": "1984",
|
||||
"author": "George Orwell",
|
||||
"published_year": 1949,
|
||||
"genres": ["Science Fiction", "Dystopian"],
|
||||
"stock": 5
|
||||
},
|
||||
{
|
||||
"title": "Pride and Prejudice",
|
||||
"author": "Jane Austen",
|
||||
"published_year": 1813,
|
||||
"genres": ["Romance", "Classic"],
|
||||
"stock": 12
|
||||
},
|
||||
{
|
||||
"title": "The Hobbit",
|
||||
"author": "J.R.R. Tolkien",
|
||||
"published_year": 1937,
|
||||
"genres": ["Fantasy", "Adventure"],
|
||||
"stock": 20
|
||||
}
|
||||
]);
|
||||
|
||||
// --- Data for: MongoDB进阶查询.md ---
|
||||
db.students.drop();
|
||||
db.students.insertMany([
|
||||
{ student_id: 'S1001', name: 'Li Wei', age: 21, major: 'Computer Science', gpa: 3.8, courses: ['Database Systems', 'Data Structures', 'Operating Systems'] },
|
||||
{ student_id: 'S1002', name: 'Zhang Min', age: 20, major: 'Computer Science', gpa: 3.6, courses: ['Algorithms', 'Computer Networks'] },
|
||||
{ student_id: 'S1003', name: 'Wang Fang', age: 22, major: 'Mathematics', gpa: 3.9, courses: ['Calculus', 'Linear Algebra'] },
|
||||
{ student_id: 'S1004', name: 'Li Juan', age: 20, major: 'Computer Science', gpa: 3.4, courses: ['Database Systems', 'Data Structures'] },
|
||||
{ student_id: 'S1005', name: 'Chen Hao', age: 23, major: 'Physics', gpa: 3.2, courses: ['Mechanics', 'Electromagnetism'] },
|
||||
{ student_id: 'S1006', name: 'Liu Yang', age: 21, major: 'Computer Science', gpa: 3.7, courses: ['Database Systems', 'Artificial Intelligence'] }
|
||||
]);
|
||||
|
||||
// --- Data for: MongoDB索引优化.md ---
|
||||
db.products_for_indexing.drop();
|
||||
db.products_for_indexing.insertMany([
|
||||
{ "category": "Electronics", "brand": "Sony", "price": 1200 },
|
||||
{ "category": "Electronics", "brand": "Samsung", "price": 950 },
|
||||
{ "category": "Electronics", "brand": "Apple", "price": 1500 },
|
||||
{ "category": "Clothing", "brand": "Nike", "price": 150 },
|
||||
{ "category": "Clothing", "brand": "Adidas", "price": 120 },
|
||||
{ "category": "Books", "brand": "PublisherA", "price": 25 },
|
||||
{ "category": "Books", "brand": "PublisherB", "price": 30 },
|
||||
{ "category": "Electronics", "brand": "Sony", "price": 800 }
|
||||
]);
|
||||
|
||||
// --- Data for: MongoDB聚合框架.md ---
|
||||
db.sales.drop();
|
||||
db.sales.insertMany([
|
||||
{ "product": "Laptop", "quantity": 1, "price": 1200, "date": new Date("2023-01-15") },
|
||||
{ "product": "Mouse", "quantity": 2, "price": 25, "date": new Date("2023-01-15") },
|
||||
{ "product": "Keyboard", "quantity": 1, "price": 75, "date": new Date("2023-01-16") },
|
||||
{ "product": "Laptop", "quantity": 1, "price": 1300, "date": new Date("2023-02-10") },
|
||||
{ "product": "Monitor", "quantity": 1, "price": 300, "date": new Date("2023-02-12") },
|
||||
{ "product": "Mouse", "quantity": 3, "price": 25, "date": new Date("2023-02-20") },
|
||||
{ "product": "Laptop", "quantity": 2, "price": 1100, "date": new Date("2023-03-05") },
|
||||
{ "product": "Webcam", "quantity": 1, "price": 50, "date": new Date("2023-03-07") }
|
||||
]);
|
||||
|
||||
db.products_for_aggregation.drop();
|
||||
db.products_for_aggregation.insertMany([
|
||||
{ "name": "Laptop", "category": "Electronics" },
|
||||
{ "name": "Mouse", "category": "Electronics" },
|
||||
{ "name": "Keyboard", "category": "Electronics" },
|
||||
{ "name": "Monitor", "category": "Electronics" },
|
||||
{ "name": "Webcam", "category": "Accessories" },
|
||||
{ "name": "T-Shirt", "category": "Apparel" }
|
||||
]);
|
||||
|
||||
// --- Data for: MongoDB数据建模.md ---
|
||||
// Note: The following commands use shell-specific functions like ObjectId() and ISODate().
|
||||
// They are intended to be run directly in the mongo shell.
|
||||
|
||||
db.users.drop();
|
||||
db.posts.drop();
|
||||
db.comments.drop();
|
||||
|
||||
const userJohnId = new ObjectId();
|
||||
db.users.insertOne({
|
||||
"_id": userJohnId,
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com"
|
||||
});
|
||||
|
||||
const postMongoId = new ObjectId();
|
||||
db.posts.insertOne({
|
||||
"_id": postMongoId,
|
||||
"author_id": userJohnId,
|
||||
"author_name": "john_doe",
|
||||
"title": "MongoDB 入门",
|
||||
"content": "MongoDB 是一个强大的 NoSQL 数据库...",
|
||||
"created_at": new Date("2024-01-15"),
|
||||
"tags": ["MongoDB", "Database", "NoSQL"]
|
||||
});
|
||||
|
||||
const userAliceId = new ObjectId();
|
||||
db.users.insertOne({
|
||||
"_id": userAliceId,
|
||||
"username": "alice",
|
||||
"email": "alice@example.com"
|
||||
});
|
||||
|
||||
db.comments.insertOne({
|
||||
"_id": new ObjectId(),
|
||||
"post_id": postMongoId,
|
||||
"user_id": userAliceId,
|
||||
"username": "alice",
|
||||
"content": "写得很好!",
|
||||
"created_at": new Date("2024-01-16")
|
||||
});
|
||||
|
||||
print("All sample data have been loaded.");
|
||||
65
数据库/MongoDB_2025/mongodb_doc.md
Normal file
65
数据库/MongoDB_2025/mongodb_doc.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# MongoDB 课程文档规范
|
||||
|
||||
本文档旨在定义 MongoDB 课程相关文件的用途、格式风格和编写原则,以确保课程内容的一致性、专业性和易读性。
|
||||
|
||||
---
|
||||
|
||||
## 1. 文件定义
|
||||
|
||||
### 1.1. 课程大纲 (`mongodb_tpl.md`)
|
||||
|
||||
- **用途**:作为 MongoDB 课程的整体教学计划和结构指南。它定义了课程的章节、知识点、学时分配和实践环节。
|
||||
- **目标读者**:课程开发者、上课教师。
|
||||
- **核心要求**:结构清晰,逻辑性强,全面覆盖 MongoDB 的核心知识体系。
|
||||
|
||||
### 1.2. 课程内容 (`n_MongoDB.md`)
|
||||
|
||||
- **用途**:根据课程大纲,详细阐述每个知识点的具体内容,包括理论讲解、代码示例、实践操作和练习题。
|
||||
- **目标读者**:学生。
|
||||
- **核心要求**:内容详实,通俗易懂,理论与实践相结合。
|
||||
|
||||
---
|
||||
|
||||
## 2. 编写原则
|
||||
|
||||
### 2.1. 内容组织:从浅入深
|
||||
|
||||
课程内容应遵循认知规律,从基础概念开始,逐步深入到高级主题和实战应用。
|
||||
|
||||
- **基础先行**:首先介绍 NoSQL 和 MongoDB 的基本概念、架构和安装部署。
|
||||
- **核心操作**:然后讲解 CRUD 操作、查询和索引等核心技能。
|
||||
- **高级进阶**:接着深入数据建模、聚合框架、副本集和分片等高级主题。
|
||||
- **实战应用**:最后通过项目实战,巩固所学知识,培养解决实际问题的能力。
|
||||
|
||||
### 2.2. 语言风格:通俗易懂
|
||||
|
||||
- **简化复杂概念**:使用简单的语言和比喻来解释复杂的技术概念。
|
||||
- **图文并茂**:适当使用图表、流程图和示意图,帮助理解抽象的知识。
|
||||
- **代码注释**:所有代码示例都应有清晰的注释,解释代码的功能和逻辑。
|
||||
|
||||
### 2.3. 理论与实践融合
|
||||
|
||||
- **理论指导实践**:每个理论知识点都应配有相应的实践案例或代码示例。
|
||||
- **实践巩固理论**:通过实验、练习和项目,加深对理论知识的理解和应用。
|
||||
- **场景驱动**:结合真实的应用场景(如电商、社交、物联网等)进行讲解,激发学习兴趣。
|
||||
|
||||
### 2.4. 格式风格:统一规范
|
||||
|
||||
- **Markdown 语法**:统一使用 Markdown 进行文档编写。
|
||||
- **标题层级**:遵循清晰的标题层级结构(`#`、`##`、`###`),与课程大纲保持一致
|
||||
- **代码块**:使用代码块来格式化代码示例,并注明语言类型。(bash/shell 统一用 shell)
|
||||
- **术语规范**:专业术语首次出现时应予以解释,并保持中英文术语的统一性。
|
||||
- **实践操作**:每一章内容最后有一个实践环节,标题为 **实践操作**,有需求描述、实践细节和结果验证。并将实践细节和结果验证放到同一个代码块中。
|
||||
|
||||
- **其他规范**:
|
||||
- 从 MongoDB 基础概念开始作为 `#` 标题,其他平级标题也用 `#` 格式
|
||||
- 不需要课程概述
|
||||
|
||||
---
|
||||
|
||||
## 3. 协作流程
|
||||
|
||||
1. **大纲评审**:首先共同评审和确定 `mongodb_tpl.md` 中的课程大纲。
|
||||
2. **内容开发**:根据大纲,分工协作编写 `n_MongoDB.md` 的具体内容。
|
||||
3. **内容评审**:定期进行内容评审(Peer Review),确保内容质量和风格统一。
|
||||
4. **持续迭代**:根据教学反馈和技术发展,持续更新和完善课程内容。
|
||||
373
数据库/MongoDB_2025/mongodb_tpl.md
Normal file
373
数据库/MongoDB_2025/mongodb_tpl.md
Normal file
@@ -0,0 +1,373 @@
|
||||
# 基础概念
|
||||
|
||||
## NoSQL 数据库概述
|
||||
|
||||
- 关系型数据库 vs NoSQL 数据库
|
||||
- NoSQL 数据库分类(文档型、键值型、列族型、图形型)
|
||||
- MongoDB 在 NoSQL 生态中的定位
|
||||
|
||||
## 核心概念
|
||||
|
||||
- 文档(Document)与 BSON 格式
|
||||
- 集合(Collection)概念
|
||||
- 数据库(Database)结构
|
||||
- MongoDB 与关系型数据库术语对比
|
||||
|
||||
## 架构原理
|
||||
|
||||
- MongoDB 存储引擎(WiredTiger)
|
||||
- 内存映射文件系统
|
||||
- 索引机制
|
||||
- 查询优化器
|
||||
|
||||
## 文档学习
|
||||
|
||||
---
|
||||
|
||||
# 环境搭建
|
||||
|
||||
## 安装部署
|
||||
|
||||
- Linux 环境安装(RockyLinux9)
|
||||
|
||||
## 相关配置
|
||||
|
||||
- 配置文件详解
|
||||
- 数据目录设置
|
||||
- 日志配置
|
||||
- 网络绑定设置
|
||||
|
||||
## 服务管理
|
||||
|
||||
- systemctl 服务管理
|
||||
- 开机自启动配置
|
||||
- 服务状态监控
|
||||
|
||||
## 实践环节
|
||||
|
||||
- 生产环境配置文件模板
|
||||
|
||||
---
|
||||
|
||||
# 基础操作
|
||||
|
||||
## MongoDB Shell 使用
|
||||
|
||||
- mongo shell 连接
|
||||
- 基本命令操作
|
||||
- 脚本执行
|
||||
|
||||
## 数据库操作
|
||||
|
||||
- 创建和删除数据库
|
||||
- 数据库列表查看
|
||||
- 数据库切换
|
||||
|
||||
## 集合操作
|
||||
|
||||
- 创建和删除集合
|
||||
- 集合属性设置
|
||||
- 集合统计信息
|
||||
|
||||
## 文档 CRUD 操作
|
||||
|
||||
- 插入文档(insert、insertOne、insertMany)
|
||||
- 查询文档(find、findOne)
|
||||
- 更新文档(update、updateOne、updateMany)
|
||||
- 删除文档(remove、deleteOne、deleteMany)
|
||||
|
||||
## 实践环节
|
||||
|
||||
- 基础 CRUD 操作练习
|
||||
- 批量数据导入导出
|
||||
- 数据迁移实验
|
||||
|
||||
---
|
||||
|
||||
# 进阶查询
|
||||
|
||||
## 查询操作符
|
||||
|
||||
- 比较操作符($eq、$ne、$gt、$gte、$lt、$lte)
|
||||
- 逻辑操作符($and、$or、$not、$nor)
|
||||
- 元素操作符($exists、$type)
|
||||
- 数组操作符($in、$nin、$all、$size)
|
||||
|
||||
## 查询修饰符
|
||||
|
||||
- 排序(sort)
|
||||
- 限制(limit)
|
||||
- 跳过(skip)
|
||||
- 投影(projection)
|
||||
|
||||
## 正则表达式查询
|
||||
|
||||
- 正则表达式语法
|
||||
- 模糊查询实现
|
||||
- 性能考虑
|
||||
|
||||
## 聚合框架基础
|
||||
|
||||
- 聚合管道概念
|
||||
- 常用聚合操作符
|
||||
- 分组和统计
|
||||
|
||||
## 实践环节
|
||||
|
||||
- 复杂查询练习
|
||||
- 聚合查询实例
|
||||
- 查询性能测试
|
||||
|
||||
---
|
||||
|
||||
# 索引优化
|
||||
|
||||
## 索引基础
|
||||
|
||||
- 索引原理和作用
|
||||
- B-Tree 索引结构
|
||||
- 索引的优缺点
|
||||
|
||||
## 索引类型
|
||||
|
||||
- 单字段索引
|
||||
- 复合索引
|
||||
- 多键索引
|
||||
- 文本索引
|
||||
- 地理空间索引
|
||||
- 哈希索引
|
||||
|
||||
## 索引管理
|
||||
|
||||
- 创建索引(createIndex)
|
||||
- 删除索引(dropIndex)
|
||||
- 查看索引(getIndexes)
|
||||
- 重建索引(reIndex)
|
||||
|
||||
## 查询性能优化
|
||||
|
||||
- 执行计划分析(explain)
|
||||
- 索引选择策略
|
||||
- 查询优化技巧
|
||||
|
||||
## 实践环节
|
||||
|
||||
- 索引创建和管理
|
||||
- 性能测试对比
|
||||
- 查询优化案例
|
||||
|
||||
---
|
||||
|
||||
# 数据建模
|
||||
|
||||
## 文档设计原则
|
||||
|
||||
- 嵌入式文档 vs 引用
|
||||
- 数据冗余策略
|
||||
- 原子性考虑
|
||||
|
||||
## Schema 设计模式
|
||||
|
||||
- 一对一关系建模
|
||||
- 一对多关系建模
|
||||
- 多对多关系建模
|
||||
- 树形结构建模
|
||||
|
||||
## 数据验证
|
||||
|
||||
- Schema 验证规则
|
||||
- 数据类型约束
|
||||
- 自定义验证器
|
||||
|
||||
## 实践环节
|
||||
|
||||
- 电商系统数据建模
|
||||
- 社交网络数据建模
|
||||
- 内容管理系统建模
|
||||
|
||||
---
|
||||
|
||||
# 聚合框架
|
||||
|
||||
## 聚合管道详解
|
||||
- $match 阶段
|
||||
- $group 阶段
|
||||
- $sort 阶段
|
||||
- $project 阶段
|
||||
- $limit 和 $skip 阶段
|
||||
|
||||
## 高级聚合操作
|
||||
|
||||
- $lookup(关联查询)
|
||||
- $unwind(数组展开)
|
||||
- $facet(多维聚合)
|
||||
- $bucket(分桶聚合)
|
||||
|
||||
## 聚合表达式
|
||||
|
||||
- 算术表达式
|
||||
- 字符串表达式
|
||||
- 日期表达式
|
||||
- 条件表达式
|
||||
|
||||
## MapReduce
|
||||
|
||||
- MapReduce 概念
|
||||
- 编写 Map 和 Reduce 函数
|
||||
- 性能对比
|
||||
|
||||
## 实践环节
|
||||
|
||||
- 数据分析报表
|
||||
- 实时统计系统
|
||||
- 复杂业务逻辑实现
|
||||
|
||||
---
|
||||
|
||||
# 副本集(Replica Set)
|
||||
|
||||
## 副本集概念
|
||||
- 高可用性需求
|
||||
- 副本集架构
|
||||
- 主从复制原理
|
||||
|
||||
## 副本集架构
|
||||
- Primary 节点
|
||||
- Secondary 节点
|
||||
- Arbiter 节点
|
||||
- 选举机制
|
||||
|
||||
## 副本集部署
|
||||
- 单机多实例部署
|
||||
- 多机部署
|
||||
- 配置文件设置
|
||||
- 初始化副本集
|
||||
|
||||
## 副本集管理
|
||||
- 添加和删除节点
|
||||
- 优先级设置
|
||||
- 故障转移测试
|
||||
- 数据同步监控
|
||||
|
||||
## 实践环节
|
||||
- 副本集搭建
|
||||
- 故障模拟和恢复
|
||||
- 性能测试
|
||||
|
||||
---
|
||||
|
||||
# 分片(Sharding)
|
||||
|
||||
## 分片概念
|
||||
- 水平扩展需求
|
||||
- 分片架构组件
|
||||
- 分片键选择
|
||||
|
||||
## 分片架构
|
||||
- Config Server
|
||||
- Shard Server
|
||||
- mongos 路由
|
||||
- 数据分布策略
|
||||
|
||||
## 分片部署
|
||||
- 分片集群搭建
|
||||
- 配置服务器部署
|
||||
- 路由服务器配置
|
||||
- 分片初始化
|
||||
|
||||
## 分片管理
|
||||
- 分片键管理
|
||||
- 数据均衡
|
||||
- 分片添加和删除
|
||||
- 性能监控
|
||||
|
||||
## 实践环节
|
||||
- 分片集群搭建
|
||||
- 数据分片测试
|
||||
- 扩容演练
|
||||
|
||||
---
|
||||
|
||||
# 安全管理
|
||||
|
||||
## 认证机制
|
||||
- 用户认证
|
||||
- 角色权限管理
|
||||
- LDAP 集成
|
||||
|
||||
## 授权控制
|
||||
- 基于角色的访问控制(RBAC)
|
||||
- 数据库级权限
|
||||
- 集合级权限
|
||||
|
||||
## 审计日志
|
||||
- 审计功能配置
|
||||
- 日志分析
|
||||
- 合规性要求
|
||||
|
||||
## 实践环节
|
||||
- 安全配置实施
|
||||
- 权限测试
|
||||
- 安全审计
|
||||
|
||||
---
|
||||
|
||||
# 备份与恢复
|
||||
|
||||
## 备份策略
|
||||
- 全量备份
|
||||
- 增量备份
|
||||
- 实时备份
|
||||
|
||||
## 备份工具
|
||||
- mongodump/mongorestore
|
||||
- 文件系统快照
|
||||
- 第三方备份工具
|
||||
|
||||
## 恢复策略
|
||||
- 完全恢复
|
||||
- 时间点恢复
|
||||
- 选择性恢复
|
||||
|
||||
## 备份自动化
|
||||
- 备份脚本编写
|
||||
- 定时任务配置
|
||||
- 备份验证
|
||||
|
||||
## 实践环节
|
||||
- 备份恢复演练
|
||||
- 自动化脚本开发
|
||||
- 灾难恢复测试
|
||||
|
||||
---
|
||||
|
||||
# 性能监控与调优
|
||||
|
||||
## 性能监控
|
||||
- mongostat 工具
|
||||
- mongotop 工具
|
||||
- 第三方监控工具
|
||||
|
||||
## 性能指标
|
||||
- 连接数监控
|
||||
- 操作延迟
|
||||
- 内存使用
|
||||
- 磁盘 I/O
|
||||
|
||||
## 性能调优
|
||||
- 查询优化
|
||||
- 索引优化
|
||||
- 硬件优化
|
||||
- 配置优化
|
||||
|
||||
## 故障诊断
|
||||
- 慢查询分析
|
||||
- 锁竞争分析
|
||||
- 内存泄漏排查
|
||||
|
||||
## 实践环节
|
||||
- 性能基准测试
|
||||
- 瓶颈分析
|
||||
- 调优实施
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user