一文掌握MongoDB使用技巧

一文掌握MongoDB使用技巧

什么是MongoDB

文档数据库: MongoDB 使用BSON 格式的文档来存储数据,而不是传统的关系型数据库中的行和列,文档可以包含复杂的数据结构,如嵌套的数组和子文档
集合: 文档被组织在集合中,类似于关系型数据库中的表,集合中的文档可以具有不同的结构
数据库: MongoDB 允许在同一实例上创建多个数据库,每个数据库中可以包含多个集合

insertOne

insertOne 方法用于向集合中插入单个文档

db.collection.insertOne(document, options)
document: 要插入的文档对象
options: 可选的配置选项,例如 writeConcern 和 bypassDocumentValidation
示例:

db.users.insertOne({
  name: "Alice",
  age: 30,
  email: "alice@example.com"
});

参数解析:
document: 这里是插入的文档 { name: “Alice”, age: 30, email: “alice@example.com” }
options: 可以设置 writeConcern(例如 { w: “majority”, wtimeout: 5000 })来定义写操作的确认级别,或者设置 bypassDocumentValidation 来绕过文档验证

使用技巧:

  • 在插入前可以使用 findOne 或 countDocuments 来检查是否存在相同的文档,避免重复插入

insertMany

insertMany 方法用于向集合中插入多个文档

db.collection.insertMany(documents, options)

documents: 要插入的文档数组
options: 可选的配置选项,例如 ordered 和 writeConcern
示例:

db.users.insertMany([
  { name: "Bob", age: 25, email: "bob@example.com" },
  { name: "Charlie", age: 35, email: "charlie@example.com" }
]);

参数解析:
documents: 一个包含多个文档的数组
options:

  • ordered: 布尔值,指定插入文档的顺序,如果设置为 true(默认),MongoDB 按顺序插入文档,遇到错误会停止插入,如果设置为 false,MongoDB 会尽可能插入所有文档,即使遇到错误也不会中止操作
  • writeConcern: 写关注级别,定义写操作的确认方式

使用技巧:

  • 使用 ordered: false 可以提高批量插入的效率,尤其是当插入的数据量较大时
  • 在插入之前使用 validate() 方法可对文档进行验证

bulkWrite

bulkWrite 方法允许对多个操作进行批量处理,包括插入、更新和删除

db.collection.bulkWrite(operations, options)

operations: 包含多个操作的数组,每个操作可以是 insertOne, updateOne, deleteOne, replaceOne 等
options: 可选的配置选项,例如 ordered 和 writeConcern
示例:

db.users.bulkWrite([
  { insertOne: { document: { name: "Dave", age: 40, email: "dave@example.com" } } },
  { insertOne: { document: { name: "Eva", age: 28, email: "eva@example.com" } } }
]);

参数解析:
operations: 一个包含多个操作的数组,每个操作对象指定了操作类型(insertOne, updateOne, deleteOne, replaceOne)及其参数
options:

  • ordered: 布尔值,指定操作的顺序处理方式,默认 true
  • writeConcern: 写关注级别,定义写操作的确认方式

使用技巧:

  • 在执行大规模的批量操作时,使用 bulkWrite 可以显著提高效率
  • 使用 ordered: false 可以避免某些操作失败导致整个批处理失败的情况

replaceOne(仅限于替换操作)

虽然 replaceOne 方法不属于插入操作,但可以结合 upsert 选项来实现插入新文档的功能

db.collection.replaceOne(filter, replacement, options)

filter: 查询条件,用于找到要替换的文档
replacement: 替换文档的新内容
options: 可选的配置选项,例如 upsert 和 writeConcern
示例:

db.users.replaceOne(
  { name: "Alice" }, 
  { name: "Alice", age: 31, email: "alice_new@example.com" },
  { upsert: true }
);

参数解析:
filter: 用于匹配文档的条件
replacement: 新的文档内容,如果文档存在,它会被完全替换;如果文档不存在且 upsert 为 true,将会插入这个新文档
options:

  • upsert: 布尔值,表示如果没有匹配的文档是否插入新文档,默认值为 false

使用技巧:

  • 使用 upsert: true 可以在文档不存在时自动插入新文档,但要注意它会完全替换匹配的文档

deleteOne

deleteOne 方法用于删除集合中匹配查询条件的第一个文档

db.collection.deleteOne(
  filter,        // 查询条件
  options        // 可选参数
)

filter: 查询条件,指定要删除的文档
options: 可选参数,如 collation(用于指定排序规则)
示例:

// 删除名字为 "Alice" 的用户
db.users.deleteOne({ name: "Alice" })

使用技巧:

  • deleteOne 只删除匹配条件的第一个文档,如果有多个文档匹配条件,只会删除第一个

deleteMany

deleteMany 方法用于删除集合中所有匹配查询条件的文档

db.collection.deleteMany(
  filter,        // 查询条件
  options        // 可选参数
)

示例:

// 删除所有年龄小于 25 的用户
db.users.deleteMany({ age: { $lt: 25 } })

使用技巧:

  • deleteMany 可以批量删除符合条件的文档,在执行删除操作之前,建议使用 find 方法检查匹配的文档,以避免意外删除重要数据

findOneAndDelete

findOneAndDelete 方法用于查找符合查询条件的文档并将其删除,然后返回被删除的文档

db.collection.findOneAndDelete(
  filter,        // 查询条件
  options        // 可选参数
)

filter: 查询条件,指定要删除的文档
options: 可选参数,例如 collation(用于指定排序规则)
示例:

// 查找并删除名字为 "Bob" 的用户,返回被删除的文档
db.users.findOneAndDelete({ name: "Bob" })

使用技巧:

  • findOneAndDelete 在删除文档的同时返回文档,适用于需要在删除时获取文档内容的场景

drop

drop 方法用于删除整个集合,这个操作会永久删除集合中的所有文档及其索引

db.collection.drop()

示例:

// 删除整个 "users" 集合
db.users.drop()

使用技巧:

  • drop 操作是不可恢复的,删除整个集合会导致所有数据丢失,使用前需确保备份数据或确认不再需要这些数据

dropDatabase

dropDatabase 方法用于删除当前数据库,这个操作会删除数据库中的所有集合及其数据

db.dropDatabase()

示例:

// 删除当前数据库
db.dropDatabase()

使用技巧:

  • dropDatabase 操作也不可恢复,执行前请确保确认需要删除整个数据库,并且已经备份重要数据

删除操作符

MongoDB 删除操作的实现并不涉及更新操作符,但在删除操作中,有时需要结合查询操作符来准确指定需要删除的文档,例如:
$lt: 查找小于某个值的文档

db.users.deleteMany({ age: { $lt: 30 } })

$gt: 查找大于某个值的文档

db.users.deleteMany({ age: { $gt: 40 } })

$in: 查找字段值在指定数组中的文档

db.users.deleteMany({ name: { $in: ["Alice", "Bob"] } })

$exists: 查找字段存在的文档

db.users.deleteMany({ address: { $exists: true } })

find

find 方法用于检索集合中的文档,可接受查询条件和投影参数

db.collection.find(query, projection)

query: 查询条件,用于筛选文档
projection: 投影参数,用于指定返回的字段
示例:

// 检索所有年龄大于 25 的用户
db.users.find({ age: { $gt: 25 } })

// 检索名字为 "Alice" 的用户,并只返回 name 和 age 字段
db.users.find({ name: "Alice" }, { name: 1, age: 1 })

使用技巧:

  • 利用索引可以显著提高查询性能,确保经常用于查询条件的字段被索引
  • 使用投影参数可以减少网络传输的数据量,只返回需要的字段

findOne

findOne 方法用于检索集合中匹配查询条件的第一个文档

db.collection.findOne(query, projection)

示例:

// 检索名字为 "Bob" 的第一个用户
db.users.findOne({ name: "Bob" })

countDocuments

countDocuments 方法用于计算匹配查询条件的文档数量

db.collection.countDocuments(query, options)

options: 可选参数,包括 limit、skip、hint 等
示例:

// 计算所有年龄大于 30 的用户数量
db.users.countDocuments({ age: { $gt: 30 } })

distinct

distinct 方法用于返回指定字段的所有不重复值

db.collection.distinct(fieldName, query, options)

fieldName: 要返回不重复值的字段名
query: 可选的查询条件
示例:

// 返回所有不同的年龄值
db.users.distinct("age")

比较操作符

MongoDB 支持多种比较操作符,如 e q (等于)、 eq(等于)、 eq(等于)、gt(大于)、 g t e (大于等于)、 gte(大于等于)、 gte(大于等于)、lt(小于)、 l t e (小于等于)、 lte(小于等于)、 lte(小于等于)、ne(不等于)、 i n (在指定数组中)、 in(在指定数组中)、 in(在指定数组中)、nin(不在指定数组中)等
示例:

// 检索年龄在 2030 之间的用户
db.users.find({ age: { $gte: 20, $lte: 30 } })

// 检索名字不是 "Alice" 的用户
db.users.find({ name: { $ne: "Alice" } })

逻辑操作符

MongoDB 支持逻辑操作符,如 a n d 、 and、 andor 和 $not,用于组合多个查询条件
示例:

// 检索年龄大于 25 且名字为 "Bob""Alice" 的用户
db.users.find({ $and: [{ age: { $gt: 25 } }, { name: { $in: ["Bob", "Alice"] } }] })

// 检索名字不是 "Bob" 且年龄不大于 30 的用户
db.users.find({ $and: [{ name: { $ne: "Bob" } }, { age: { $not: { $gt: 30 } } }] })

元素操作符

对于数组字段,MongoDB 提供了元素操作符,如 e x i s t s (检查字段是否存在)、 exists(检查字段是否存在)、 exists(检查字段是否存在)、type(检查字段类型)等
示例:

// 检索拥有 email 字段的用户
db.users.find({ email: { $exists: true } })

正则表达式

MongoDB 支持使用正则表达式进行模式匹配查询
示例1:

// 检索名字以 "A" 开头的用户
db.users.find({ name: { $regex: "^A" } }):

示例2:

db.collection.find({ field: { $regex: /pattern/, $options: 'i' } })

pattern: 正则表达式模式
$options: 可选标志,例如 ‘i’ 表示不区分大小写

// 检索名字中包含 "smith" 的用户,不区分大小写
db.users.find({ name: { $regex: "smith", $options: 'i' } })

数组操作符

MongoDB 提供了用于查询数组字段的操作符,如 e l e m M a t c h 、 elemMatch、 elemMatchsize 等
$elemMatch: 用于匹配数组中符合多个条件的元素

db.collection.find({ arrayField: { $elemMatch: { condition } } })

示例:

// 查找数组中包含 age 大于 25 的用户
db.users.find({ scores: { $elemMatch: { age: { $gt: 25 } } } })

$size: 用于匹配数组的大小,

db.collection.find({ arrayField: { $size: n } })

示例:

// 查找数组长度为 3 的用户
db.users.find({ scores: { $size: 3 } })

地理空间查询

MongoDB 提供了地理空间查询功能,支持使用 2d、2dsphere 索引进行地理位置查询
$near: 查找距离指定点最近的文档

db.collection.find({ location: { $near: { $geometry: { type: "Point", coordinates: [longitude, latitude] }, $maxDistance: distance } } })

示例:

// 查找距离指定点 (40.7128, -74.0060) 最近的文档,距离不超过 5 公里
db.places.find({
  location: {
    $near: {
      $geometry: {
        type: "Point",
        coordinates: [-74.0060, 40.7128]
      },
      $maxDistance: 5000
    }
  }
})

$geoWithin: 查找位于指定地理区域内的文档

db.collection.find({ location: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [ [ [lng, lat], ... ] ] } } })

示例:

// 查找位于指定多边形区域内的文档
db.places.find({
  location: {
    $geoWithin: {
      $geometry: {
        type: "Polygon",
        coordinates: [[[-74.007, 40.712], [-74.005, 40.710], [-74.003, 40.712], [-74.007, 40.712]]]
      }
    }
  }
})

聚合查询

MongoDB 的聚合框架允许用户执行复杂的数据处理和分析操作
$match: 用于过滤数据,类似于 find 方法的查询条件

db.collection.aggregate([
  { $match: { condition } }
])

示例:

// 聚合查询中过滤年龄大于 25 的用户
db.users.aggregate([
  { $match: { age: { $gt: 25 } } }
])

$group: 用于将文档分组,并对分组结果进行汇总

db.collection.aggregate([
  { $group: { _id: "$field", total: { $sum: "$anotherField" } } }
])

示例:

// 按年龄分组并计算每组的用户总数
db.users.aggregate([
  { $group: { _id: "$age", totalUsers: { $sum: 1 } } }
])

$sort: 用于对聚合结果进行排序

db.collection.aggregate([
  { $sort: { field: 1 } }
])

示例:

// 按年龄升序排序
db.users.aggregate([
  { $sort: { age: 1 } }
])

$project: 用于控制输出文档的字段

db.collection.aggregate([
  { $project: { field1: 1, field2: 1 } }
])

示例:

// 只返回名字和年龄字段
db.users.aggregate([
  { $project: { name: 1, age: 1 } }
])

游标操作

MongoDB 的查询操作会返回游标对象,允许你对结果进行分页和迭代
skip: 跳过指定数量的文档,用于分页

db.collection.find().skip(n)

示例:

// 跳过前 10 个文档,返回接下来的 5 个文档
db.users.find().skip(10).limit(5)

limit: 限制返回的文档数量

db.collection.find().limit(n)

示例:

// 只返回前 5 个文档
db.users.find().limit(5)

updateOne

updateOne 方法用于更新集合中匹配查询条件的第一个文档

db.collection.updateOne(
  filter,        // 查询条件
  update,        // 更新操作
  options        // 可选参数
)

filter: 查询条件,指定需要更新的文档
update: 更新操作,指定更新的内容和操作符
options: 可选参数,例如 upsert(是否插入新文档)
示例:

// 更新名字为 "Alice" 的用户的年龄为 30
db.users.updateOne(
  { name: "Alice" },
  { $set: { age: 30 } }
)

使用技巧:

  • 使用 $set 操作符可以更新指定字段的值,不会影响其他字段
  • 使用 $inc 操作符可以增加字段的值,而不是直接设置

updateMany

updateMany 方法用于更新集合中所有匹配查询条件的文档

db.collection.updateMany(
  filter,        // 查询条件
  update,        // 更新操作
  options        // 可选参数
)

示例:

// 将所有年龄小于 25 的用户的状态更新为 "年轻"
db.users.updateMany(
  { age: { $lt: 25 } },
  { $set: { status: "年轻" } }
)

使用技巧:

  • updateMany 可以批量更新文档,注意检查更新条件以确保只更新所需的文档

replaceOne

replaceOne 方法用于替换集合中匹配查询条件的第一个文档,新的文档完全替换旧文档

db.collection.replaceOne(
  filter,        // 查询条件
  replacement,   // 新文档
  options        // 可选参数
)

filter: 查询条件,指定需要替换的文档
replacement: 新文档,完全替换匹配文档
options: 可选参数,例如 upsert(是否插入新文档)
示例:

// 将名字为 "Bob" 的用户文档完全替换为新文档
db.users.replaceOne(
  { name: "Bob" },
  { name: "Bob", age: 28, status: "更新" }
)

使用技巧:

  • replaceOne 操作会替换整个文档,使用时需确保新文档包含所有必需的字段

findOneAndUpdate

findOneAndUpdate 用于查找符合查询条件的文档,并对其进行更新,然后返回更新后的文档或更新前的文档

db.collection.findOneAndUpdate(
  filter,        // 查询条件
  update,        // 更新操作
  options        // 可选参数
)

filter: 查询条件,指定需要更新的文档
update: 更新操作,指定更新的内容和操作符
options: 可选参数,例如 returnOriginal(是否返回更新前的文档)
示例:

// 更新名字为 "Charlie" 的用户的状态为 "活跃",并返回更新后的文档
db.users.findOneAndUpdate(
  { name: "Charlie" },
  { $set: { status: "活跃" } },
  { returnOriginal: false }
)

使用技巧:

  • findOneAndUpdate 允许在更新操作的同时获取文档,可以用于在更新时获取相关数据

findOneAndReplace

findOneAndReplace 用于查找符合查询条件的文档,并用新文档替换,然后返回更新后的文档或更新前的文档

db.collection.findOneAndReplace(
  filter,        // 查询条件
  replacement,   // 新文档
  options        // 可选参数
)

示例:

// 查找名字为 "David" 的用户,并用新文档替换,返回更新后的文档
db.users.findOneAndReplace(
  { name: "David" },
  { name: "David", age: 35, status: "新状态" },
  { returnOriginal: false }
)

使用技巧:

  • findOneAndReplace 可以用于在替换文档时获取详细数据,有助于跟踪和验证替换操作

findOneAndDelete

findOneAndDelete 方法用于查找符合查询条件的文档,并将其删除,然后返回被删除的文档

db.collection.findOneAndDelete(
  filter,        // 查询条件
  options        // 可选参数
)

示例:

// 查找并删除名字为 "Eve" 的用户,返回被删除的文档
db.users.findOneAndDelete(
  { name: "Eve" }
)

使用技巧:

  • findOneAndDelete 适用于需要在删除文档时获取被删除数据的场景

更新操作符

MongoDB 提供了多种更新操作符,以便执行不同类型的更新操作:
$set: 设置字段的值

db.users.updateOne({ name: "Alice" }, { $set: { age: 30 } })

$unset: 删除字段

db.users.updateOne({ name: "Alice" }, { $unset: { address: "" } })

$inc: 增加字段的值

db.users.updateOne({ name: "Alice" }, { $inc: { age: 1 } })

$push: 将元素添加到数组字段

db.users.updateOne({ name: "Alice" }, { $push: { scores: 90 } })

$pull: 从数组字段中删除匹配的元素

db.users.updateOne({ name: "Alice" }, { $pull: { scores: 90 } })

$addToSet: 将元素添加到数组字段中,但不会添加重复的元素

db.users.updateOne({ name: "Alice" }, { $addToSet: { scores: 90 } })

$rename: 重命名字段

db.users.updateOne({ name: "Alice" }, { $rename: { oldField: "newField" } })

索引

单字段索引(Single Field Index)

单字段索引是最基本的索引类型,创建在集合的一个字段上

db.collection.createIndex(
  { field: 1 }   // 创建升序索引,降序索引使用 -1
)

示例:

// 在 "age" 字段上创建升序索引
db.users.createIndex({ age: 1 })

使用技巧:

  • 对于单字段查询条件,单字段索引通常能够提供良好的性能

复合索引(Compound Index)

复合索引是基于多个字段的索引,这种索引可以提高对多个字段的查询效率

db.collection.createIndex(
  { field1: 1, field2: -1 }   // 字段1按升序索引,字段2按降序索引
)

示例:

// 在 "age""name" 字段上创建复合索引
db.users.createIndex({ age: 1, name: -1 })

使用技巧:

  • 复合索引的字段顺序很重要,MongoDB 会利用索引中前缀的字段来加速查询
  • 复合索引可以加速多字段查询,但请确保字段顺序与查询条件一致

唯一索引(Unique Index)

唯一索引保证索引字段中的所有值都是唯一的,适用于需要唯一约束的场景

db.collection.createIndex(
  { field: 1 },   // 升序索引
  { unique: true }  // 唯一索引选项
)

示例:

// 在 "email" 字段上创建唯一索引,确保每个文档的 email 唯一
db.users.createIndex({ email: 1 }, { unique: true })

使用技巧:

  • 唯一索引确保字段值唯一,适用于用户名、邮箱等唯一标识符的字段
  • 创建唯一索引时,如果集合中已有重复数据,创建索引会失败

稀疏索引(Sparse Index)

稀疏索引只为那些包含索引字段的文档创建索引,不为字段缺失的文档创建索引

db.collection.createIndex(
  { field: 1 },   // 升序索引
  { sparse: true }  // 稀疏索引选项
)

示例:

// 在 "address" 字段上创建稀疏索引
db.users.createIndex({ address: 1 }, { sparse: true })

使用技巧:

  • 稀疏索引适用于字段不是每个文档都有的情况
  • 可以节省存储空间和提高查询效率,但不适用于所有情况,特别是当你需要索引所有文档时

部分索引(Partial Index)

部分索引允许创建索引仅覆盖符合特定条件的文档

db.collection.createIndex(
  { field: 1 },   // 升序索引
  { partialFilterExpression: { condition } }  // 部分索引条件
)

示例:

// 创建一个部分索引,只为 "age" 大于 30 的文档创建索引
db.users.createIndex(
  { age: 1 },
  { partialFilterExpression: { age: { $gt: 30 } } }
)

使用技巧:

  • 部分索引可以提高对特定子集文档的查询效率
  • 在设计部分索引时,要确保查询条件与部分索引条件一致

地理空间索引(Geospatial Index)

地理空间索引用于支持地理位置相关的查询,如距离计算、位置查询等

db.collection.createIndex(
  { location: "2dsphere" }   // 创建 2dsphere 索引
)

示例:

// 在 "location" 字段上创建 2dsphere 索引,用于支持地理位置查询
db.places.createIndex({ location: "2dsphere" })

使用技巧:

  • 2dsphere 索引支持地球坐标的查询,如距离计算和位置查询
  • 创建地理空间索引时,请确保文档中的地理坐标格式符合要求

文本索引(Text Index)

文本索引用于全文搜索,支持对文本字段的搜索操作

db.collection.createIndex(
  { field: "text" }   // 创建文本索引
)

示例:

// 在 "description" 字段上创建文本索引
db.articles.createIndex({ description: "text" })

使用技巧:

  • 文本索引支持对字段中的文本进行全文搜索,适用于需要查找关键词的场景
  • 文本索引支持 $text 查询操作,如 $text 查找和 $text.score 排序

哈希索引(Hashed Index)

哈希索引用于支持对字段值的哈希散列,适用于分片键的索引

db.collection.createIndex(
  { field: "hashed" }   // 创建哈希索引
)

示例:

// 在 "user_id" 字段上创建哈希索引
db.orders.createIndex({ user_id: "hashed" })

使用技巧:

  • 哈希索引用于确保分片键的均匀分布,适用于分片数据集
  • 不支持范围查询,适用于精确匹配查询

聚合函数

$match

$match 阶段用于筛选文档,类似于 SQL 中的 WHERE 子句,它根据指定的条件过滤文档
语法:

{ $match: { <query> } }

示例:

// 查找年龄大于 25 的用户
db.users.aggregate([
  { $match: { age: { $gt: 25 } } }
])

使用技巧:

  • $match 通常是聚合管道中的第一个阶段,以尽早过滤掉不相关的文档,减少后续操作的计算量

$group

$group 阶段用于将文档分组,并对每个组进行聚合操作,如计算总和、平均值,类似于 SQL 中的 GROUP BY 子句

{ $group: { 
    _id: <expression>,  // 分组字段或表达式
    <field1>: { <accumulator1> : <expression> },  // 聚合操作
    <field2>: { <accumulator2> : <expression> }
} }

示例:

// 计算每个年龄段的用户数量
db.users.aggregate([
  { $group: { 
      _id: "$age",  // 按年龄分组
      count: { $sum: 1 }  // 统计每个年龄的用户数量
  } }
])

使用技巧:

  • _id 字段用于指定分组字段,可以是字段名,也可以是计算表达式
  • 常用的累加器有 s u m 、 sum、 sumavg、 m a x 、 max、 maxmin、 f i r s t 、 first、 firstlast 等

$sort

$sort 阶段用于对文档进行排序,类似于 SQL 中的 ORDER BY 子句

{ $sort: { <field1>: <sortOrder1>, <field2>: <sortOrder2> } }

sortOrder 1(升序)或 -1(降序)
示例:

// 按年龄降序排序用户
db.users.aggregate([
  { $sort: { age: -1 } }
])

使用技巧:

  • 在排序之前使用 $match 阶段过滤数据,以减少需要排序的数据量
  • 多字段排序时可以指定多个排序条件

$project

$project 阶段用于指定要包含或排除的字段,类似于 SQL 中的 SELECT 子句

{ $project: { <field1>: <include|exclude>, <field2>: <include|exclude> } }

include: 1 或 true(包含字段)
exclude: 0 或 false(排除字段)
示例:

// 仅保留用户名和年龄字段
db.users.aggregate([
  { $project: { username: 1, age: 1 } }
])

使用技巧:

  • $project 可以用于重命名字段、计算新字段以及控制输出字段

$limit

$limit 阶段用于限制文档的数量,类似于 SQL 中的 LIMIT 子句

{ $limit: <number> }

示例:

// 限制结果为前 10 个文档
db.users.aggregate([
  { $limit: 10 }
])

使用技巧:

  • 通常在排序之后使用 $limit 以获取前 N 条记录
  • 适用于需要分页显示数据的场景

$skip

$skip 阶段用于跳过指定数量的文档,类似于 SQL 中的 OFFSET 子句

{ $skip: <number> }

示例:

// 跳过前 5 个文档,返回后续文档
db.users.aggregate([
  { $skip: 5 }
])

使用技巧:

  • 通常与 $limit 一起使用,用于分页显示数据
  • 在大数据集上使用 $skip 可能会影响性能

$unwind

$unwind 阶段用于将数组字段中的每个元素拆分为单独的文档,类似于 SQL 中的 JOIN 操作

{ $unwind: <path> }

path: 要拆分的数组字段路径(以 $ 开头)
示例:

// 将 "items" 数组字段中的每个元素拆分为单独的文档
db.orders.aggregate([
  { $unwind: "$items" }
])

使用技巧:

  • $unwind 可以与 g r o u p 、 group、 groupsort 等操作结合使用,以对数组元素进行聚合和排序
  • 使用 $unwind 时,请确保数组字段存在于文档中

$lookup

$lookup 阶段用于执行左连接操作,将来自其他集合的数据合并到当前集合中

{ $lookup: {
    from: <collection>,  // 要连接的集合
    localField: <field>,  // 当前集合中的字段
    foreignField: <field>,  // 目标集合中的字段
    as: <arrayField>  // 合并结果存储的字段名
} }

示例:

// 从 "products" 集合中查找与 "orders" 集合中的 "product_id" 匹配的文档
db.orders.aggregate([
  { $lookup: {
      from: "products",
      localField: "product_id",
      foreignField: "product_id",
      as: "product_details"
  } }
])

使用技巧:

  • $lookup 可以用于实现复杂的连接操作,但性能可能受到影响,特别是对大集合的操作
  • 如果需要更高效的连接,可以考虑使用 MongoDB Atlas Data Lake 或 MongoDB Stitch 等工具

$addFields

$addFields 阶段用于添加新字段或更新现有字段的值

{ $addFields: { <field>: <expression> } }
// 为每个用户添加 "full_name" 字段,值为 "first_name""last_name" 的组合
db.users.aggregate([
  { $addFields: { full_name: { $concat: ["$first_name", " ", "$last_name"] } } }
])

使用技巧:

  • $addFields 可以用来计算新字段,或修改现有字段的值
  • p r o j e c t 不同, project 不同, project不同,addFields 可以保留原有字段,而不仅仅是控制输出字段

$replaceRoot

$replaceRoot 阶段用于替换文档的根元素,可以将子文档提升为根文档

{ $replaceRoot: { newRoot: <expression> } }

示例:

// 将 "product_details" 子文档替换为根文档
db.orders.aggregate([
  { $lookup: {
      from: "products",
      localField: "product_id",
      foreignField: "product_id",
      as: "product_details"
  } },
  { $unwind: "$product_details" },
  { $replaceRoot: { newRoot: "$product_details" } }
])

使用技巧:

  • $replaceRoot 用于将子文档提升为根文档,以简化文档结构
  • 在复杂的聚合管道中,可以帮助优化文档结构

$merge

$merge 阶段用于将聚合结果写入新集合或现有集合中

{ $merge: { into: <collection>, whenMatched: <strategy>, whenNotMatched: <strategy> } }

into: 目标集合名称
whenMatched: 匹配文档的处理策略(如 merge、replace、keepExisting)
whenNotMatched: 没有匹配文档的处理策略(如 insert)
示例:

// 将聚合结果写入 "summary" 集合
db.orders.aggregate([
  { $group: { _id: "$product_id", total_sales: { $sum: "$quantity" } } },
  { $merge: { into: "summary" } }
])

使用技巧:

  • $merge 用于将聚合结果持久化到数据库集合中
  • 可以选择匹配和不匹配文档的处理策略,以控制如何将数据写入目标集合

$count

$count 阶段用于计算文档的数量,并将结果作为单独的字段返回

{ $count: <countField> }

countField: 用于返回文档数量的字段名称
示例:

// 计算符合条件的用户数量
db.users.aggregate([
  { $match: { age: { $gt: 25 } } },
  { $count: "total_users" }
])

使用技巧:

  • $count 简化了计算总数的操作,适用于需要获取总数量的场景
  • 在大数据集上使用 $count 时,可以先使用 $match 过滤数据,以提高性能

聚合管道优化

尽早使用$match

将 $match 阶段尽早放在聚合管道中,以减少处理的数据量,可显著提高后续阶段的性能
示例:

// 优化前:不先过滤数据
db.orders.aggregate([
  { $group: { _id: "$product_id", total_sales: { $sum: "$quantity" } } },
  { $sort: { total_sales: -1 } }
])

// 优化后:先过滤数据,再进行聚合
db.orders.aggregate([
  { $match: { order_date: { $gte: new Date('2024-01-01') } } },
  { $group: { _id: "$product_id", total_sales: { $sum: "$quantity" } } },
  { $sort: { total_sales: -1 } }
])

技巧:

  • 将 $match 放在聚合管道的开始位置,确保过滤条件能够尽早减少数据集的大小

使用$project控制字段

在聚合管道中使用 $project 仅保留必要的字段,减少数据传输和处理的开销
示例:

// 优化前:不控制输出字段
db.orders.aggregate([
  { $match: { status: "shipped" } },
  { $group: { _id: "$product_id", total_quantity: { $sum: "$quantity" } } },
  { $sort: { total_quantity: -1 } }
])

// 优化后:仅保留需要的字段
db.orders.aggregate([
  { $match: { status: "shipped" } },
  { $project: { product_id: 1, quantity: 1 } },
  { $group: { _id: "$product_id", total_quantity: { $sum: "$quantity" } } },
  { $sort: { total_quantity: -1 } }
])

技巧:

  • 使用 $project 可以减少数据在管道中传递的大小,从而提高性能

使用 l i m i t 和 limit和 limitskip分页

通过 $limit 和 $skip 实现分页,以减少对大量数据的处理
示例:

// 获取前 10 条记录
db.orders.aggregate([
  { $sort: { order_date: -1 } },
  { $limit: 10 }
])

// 分页示例:获取第 2 页的数据,每页 10 条记录
db.orders.aggregate([
  { $sort: { order_date: -1 } },
  { $skip: 10 },  // 跳过第一页数据
  { $limit: 10 }  // 获取第二页数据
])

技巧:

  • 使用 $limit 和 $skip 进行分页,避免在没有索引的字段上进行大量的数据扫描
  • $skip 在大数据集上可能会影响性能,考虑使用更高效的分页策略,例如基于游标的分页

索引优化

确保在聚合管道中使用的字段上创建适当的索引,尤其是在 $match 和 $sort 阶段涉及的字段
示例:

// 创建索引以优化查询
db.orders.createIndex({ order_date: -1 })

技巧:

  • 在 $match 和 $sort 阶段涉及的字段上创建索引,能够显著提高性能
  • 使用 explain() 方法检查聚合查询的性能,了解索引的使用情况

优化$lookup使用

$lookup 操作可能会导致性能问题,特别是在处理大型集合时,使用 $lookup 时考虑以下优化策略:

  • 使用 $lookup的pipeline参数:仅在需要的字段上执行连接,并进行必要的过滤操作
    示例:
// 优化前:直接连接
db.orders.aggregate([
  { $lookup: {
      from: "products",
      localField: "product_id",
      foreignField: "product_id",
      as: "product_details"
  } }
])

// 优化后:使用 pipeline 进行优化
db.orders.aggregate([
  { $lookup: {
      from: "products",
      let: { product_id: "$product_id" },
      pipeline: [
        { $match: { $expr: { $eq: ["$product_id", "$$product_id"] } } },
        { $project: { product_name: 1, price: 1 } }
      ],
      as: "product_details"
  } }
])

技巧:

  • 使用 $lookup 的 pipeline 参数可以减少合并的数据量,仅保留所需字段
  • 确保连接字段上有索引,以提高连接性能

避免过度使用$unwind

$unwind 阶段会将数组字段拆分为多个文档,这可能导致大量的中间结果,尽量减少不必要的 $unwind 使用
示例:

// 优化前:拆分数组字段后进行聚合
db.orders.aggregate([
  { $unwind: "$items" },
  { $group: { _id: "$items.product_id", total_quantity: { $sum: "$items.quantity" } } }
])

// 优化后:避免不必要的拆分
db.orders.aggregate([
  { $project: { items: 1 } },
  { $group: { _id: "$_id", total_quantity: { $sum: "$items.quantity" } } }
])

技巧:

  • 避免在不必要的情况下使用 $unwind,如果仅需要处理数组的部分数据,考虑其他优化方式
  • 在大数组上使用 $unwind 可能会导致性能问题,尤其是在没有索引的情况下

管道顺序优化

合理安排聚合管道中的阶段顺序,以确保高效的数据处理
示例:

// 优化前:顺序不合理
db.orders.aggregate([
  { $group: { _id: "$product_id", total_sales: { $sum: "$quantity" } } },
  { $match: { total_sales: { $gt: 100 } } },
  { $sort: { total_sales: -1 } }
])

// 优化后:合理排序
db.orders.aggregate([
  { $match: { order_date: { $gte: new Date('2024-01-01') } } },
  { $group: { _id: "$product_id", total_sales: { $sum: "$quantity" } } },
  { $match: { total_sales: { $gt: 100 } } },
  { $sort: { total_sales: -1 } }
])

技巧:

  • 将 $match 和 $sort 放在聚合管道的合适位置,减少不必要的计算
  • 按照操作逻辑的顺序安排管道阶段,以提高整体性能

使用$facet分析多视角数据

$facet 阶段可以在一个管道中并行处理多个聚合操作,减少多个管道查询的开销
示例:

db.orders.aggregate([
  { $facet: {
      totalSales: [
        { $group: { _id: "$product_id", total_quantity: { $sum: "$quantity" } } }
      ],
      orderCount: [
        { $count: "total_orders" }
      ]
  } }
])

技巧:

  • 使用 $facet 进行多视角的数据分析,可以减少多个单独聚合查询的开销
  • 注意 $facet 阶段的内存使用情况,确保足够的资源来处理并行操作

监控和调优

使用 MongoDB 的性能监控工具,如 explain() 方法,来分析和调优聚合查询
示例:

// 使用 explain() 方法检查聚合查询的性能
db.orders.aggregate([
  { $match: { status: "shipped" } },
  { $group: { _id: "$product_id", total_quantity: { $sum: "$quantity" } } }
]).explain("executionStats")

技巧:

  • 通过 explain() 方法分析查询的执行计划和性能瓶颈
  • 根据 explain() 的输出信息优化索引和管道顺序

主从复制

MongoDB 的主从复制(Replication)是一种数据冗余和高可用性的机制,可保证数据的备份和恢复能力,、,主从复制允许在一个 MongoDB 集群中设置主节点(Primary)和从节点(Secondary),从节点会自动从主节点同步数据

主从复制概述

MongoDB 的主从复制基于副本集(Replica Set)架构,副本集是 MongoDB 提供的一种容错机制,由一个主节点和多个从节点组成,副本集通过将数据从主节点复制到从节点来实现数据冗余,副本集确保在主节点出现故障时,系统能够继续正常运行

主要概念

主节点(Primary): 处理所有的读写请求,并将数据写入到其数据集,只有主节点可以进行写操作
从节点(Secondary): 从主节点同步数据,主要用于处理读请求和数据备份,在主节点故障时,从节点可以被选举为新的主节点
仲裁节点(Arbiter): 不存储数据,只参与选举过程,以确保副本集的选举流程顺利进行,仲裁节点可以帮助确定哪个节点成为新的主节点,但不参与数据同步

设置副本集

1. 基本步骤

1. 启动 MongoDB 实例
启动多个 MongoDB 实例,确保它们能够进行网络通信

mongod --replSet "myReplicaSet" --dbpath /data/db1 --port 27017
mongod --replSet "myReplicaSet" --dbpath /data/db2 --port 27018
mongod --replSet "myReplicaSet" --dbpath /data/db3 --port 27019

2. 初始化副本集
连接到主节点,使用 rs.initiate() 初始化副本集

// 连接到主节点
mongo --port 27017

// 初始化副本集
rs.initiate({
  _id: "myReplicaSet",
  members: [
    { _id: 0, host: "localhost:27017" },
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019" }
  ]
})

3. 检查副本集状态
使用 rs.status() 检查副本集的状态,确认副本集配置正确,节点状态正常

rs.status()

2. 参数解析

_id: 副本集的名称
members: 副本集的成员列表,每个成员需要指定 _id 和 host,_id 是成员的唯一标识符,host 是成员的主机和端口

3. 配置副本集选项

副本集可以通过修改配置来调整其行为
1. 更改成员配置
连接到副本集的主节点,使用 rs.reconfig() 修改副本集配置

var config = rs.conf();
config.members[0].priority = 2; // 提高主节点的优先级
rs.reconfig(config)

2. 设置读写分离
通过读取副本集中的从节点进行读操作,可以提高系统的读性能,可以在应用程序中配置读取偏好

// 连接到副本集
var db = connect("mongodb://localhost:27017,localhost:27018,localhost:27019/mydb");

// 设置读偏好为从节点
db.getMongo().setReadPref("secondaryPreferred");

读偏好选项:
primary: 所有读取操作都发送到主节点
primaryPreferred: 优先读取主节点,如果主节点不可用,则从从节点读取
secondary: 所有读取操作都发送到从节点
secondaryPreferred: 优先从从节点读取,如果从节点不可用,则从主节点读取
nearest: 从最接近的节点读取

3. 配置仲裁节点
仲裁节点不存储数据,只参与副本集的选举过程,可以在副本集中添加仲裁节点来提高选举的稳定性

// 添加仲裁节点
rs.addArb("localhost:27020")

4. 备份和恢复

副本集可以用于备份和恢复数据,可以从从节点中备份数据,主节点的备份会导致性能问题

备份
# 使用 mongodump 进行备份
mongodump --host localhost --port 27019 --out /backup/backup1
恢复
# 使用 mongorestore 恢复数据
mongorestore --host localhost --port 27017 /backup/backup1

5. 故障恢复

副本集自动处理主节点故障,进行选举产生新的主节点,副本集中的从节点会同步数据,保持数据一致性

手动故障恢复

1. 强制重新选举
如果需要手动强制重新选举主节点,可以使用 rs.stepDown() 命令

// 强制当前主节点下线
rs.stepDown()

2. 添加或删除节点
添加或删除副本集成员,使用 rs.add() 和 rs.remove() 命令

// 添加节点
rs.add("localhost:27020")

// 删除节点
rs.remove("localhost:27019")

分片

MongoDB 的分片(Sharding)是一种横向扩展(scaling out)技术,用于处理大规模数据集和高吞吐量的应用场景,通过分片,可以将数据分布到多个节点上,从而提高数据存储能力和查询性能

1. 分片概述

分片: 一个 MongoDB 实例或副本集,存数据的实际副本,数据根据分片键(Shard Key)分布在不同的分片中
分片键: 用于将数据分配到不同分片的字段,选择合适的分片键对分片系统的性能至关重要
配置服务器: 存储分片元数据和路由信息的 MongoDB 实例,分片系统的所有操作都依赖于配置服务器
路由服务: 客户端与分片集群交互的入口,它负责根据分片键将请求路由到相应的分片

2. 设置分片

1. 启动配置服务器
启动配置服务器,通常配置服务器是副本集,假设配置服务器在端口 27019

mongod --configsvr --replSet "configReplSet" --dbpath /data/configdb --port 27019

2. 启动分片
启动多个分片节点,通常每个分片是一个副本集,假设分片节点在端口 27017、27018 和 27020

mongod --shardsvr --replSet "shard1" --dbpath /data/shard1 --port 27017
mongod --shardsvr --replSet "shard2" --dbpath /data/shard2 --port 27018
mongod --shardsvr --replSet "shard3" --dbpath /data/shard3 --port 27020

3. 启动路由服务
启动 mongos 路由服务,mongos 负责将客户端请求路由到正确的分片

mongos --configdb configReplSet/localhost:27019 --port 27017

4. 初始化配置
连接到 mongos 路由服务并初始化分片集群

// 连接到 mongos
mongo --port 27017

// 添加分片
sh.addShard("shard1/localhost:27017")
sh.addShard("shard2/localhost:27018")
sh.addShard("shard3/localhost:27020")

// 添加分片集的配置
sh.enableSharding("mydatabase")

解析:
sh.addShard(): 将分片添加到分片集群
sh.enableSharding(): 启用数据库的分片功能

5. 选择分片键并分片集合
为要分片的集合选择分片键,并启用集合的分片

// 选择分片键并分片集合
sh.shardCollection("mydatabase.mycollection", { "shardKeyField": 1 })

解析:

  • sh.shardCollection():为集合选择分片键,并启用分片,shardKeyField 是用于分片的字段

3. 参数解析

配置服务器

–configsvr: 指定实例作为配置服务器
–replSet “configReplSet”: 配置服务器副本集的名称
–dbpath: 数据存储路径
–port: 配置服务器的端口

分片

–shardsvr: 指定实例作为分片
–replSet “shard1”: 分片副本集的名称
–dbpath: 数据存储路径
–port: 分片的端口

路由服务

–configdb: 配置服务器副本集的连接字符串
–port: mongos 的端口

4. 使用技巧

选择分片键

选择高基数字段: 分片键应该具有高基数,以确保数据均匀分布在所有分片上
避免热点: 选择写操作频繁的字段作为分片键可能会导致某些分片负载过重,选择写操作分布均匀的字段作为分片键
预估数据增长: 选择适合数据增长模式的分片键,以避免后期的重新分片操作

监控和维护

监控分片性能: 使用 MongoDB 的监控工具(如 mongostat 和 mongotop)来监控分片集群的性能和负载
重新平衡数据: 如果某个分片的数据量显著大于其他分片,可以使用 sh.moveChunk() 手动平衡数据

// 手动移动数据块
sh.moveChunk("mydatabase.mycollection", { "shardKeyField": 10 }, "shard2")

解析:

  • sh.moveChunk():将特定的数据块移动到指定的分片
  • 定期维护:定期检查分片集群的状态,确保分片的正常运行

备份和恢复

备份: 使用 mongodump 和 mongorestore 进行备份和恢复,确保备份和恢复操作不会影响分片的正常运行

# 备份
mongodump --host localhost --port 27017 --out /backup

# 恢复
mongorestore --host localhost --port 27017 /backup

跨分片备份: 备份时,确保所有分片的数据都被备份,避免数据不一致

5. 故障处理

分片故障: 如果某个分片发生故障,可以将故障分片从集群中移除并修复,然后重新添加

// 移除分片
sh.removeShard("shard3")

// 修复并重新添加
sh.addShard("shard3/localhost:27020")

配置服务器故障: 配置服务器副本集的故障可以通过副本集机制自动恢复

事务

MongoDB 的事务功能允许在多个操作之间实现 ACID(原子性、一致性、隔离性、持久性)特性,确保数据的完整性和一致性,事务在处理涉及多个文档或集合的操作时尤其重要

1. 事务概述

事务: 一组操作,这些操作要么全部成功,要么全部失败,事务确保所有操作都以原子性方式执行,即要么所有操作都生效,要么全部回滚
原子性: 事务中的所有操作要么全部成功,要么全部失败,没有中间状态
一致性: 事务完成后,数据库必须保持一致性状态
隔离性: 事务对其他事务的操作是隔离的,确保事务之间不会相互干扰
持久性: 一旦事务提交,其结果是持久的,即使系统崩溃也能保持

2. 使用事务

MongoDB 支持两种类型的事务:
单文档事务: 单文档事务是一种简单的事务,保证单个文档的原子性操作,MongoDB 中的所有写操作本身都是原子的,因此单文档事务通常不需要显式事务支持
多文档事务: 用于跨多个文档、集合甚至数据库的操作,支持更复杂的操作

多文档事务的使用示例

1. 启动客户端会话
要使用多文档事务,首先需要在客户端创建一个会话(Session)

const session = client.startSession();

2. 开始事务
使用 startTransaction 方法开始事务

session.startTransaction();

3. 执行操作
在事务中执行所需的数据库操作,所有操作必须使用同一个会话对象

try {
    const collection1 = client.db('mydatabase').collection('collection1');
    const collection2 = client.db('mydatabase').collection('collection2');

    // 执行写操作
    await collection1.insertOne({ _id: 1, field: 'value' }, { session });
    await collection2.updateOne({ _id: 2 }, { $set: { field: 'new value' } }, { session });

    // 提交事务
    await session.commitTransaction();
} catch (error) {
    // 回滚事务
    await session.abortTransaction();
    console.error('Transaction aborted due to an error:', error);
} finally {
    // 结束会话
    session.endSession();
}

解析:
startSession(): 启动一个会话
startTransaction(): 在会话中开始事务
commitTransaction(): 提交事务,将所有操作持久化
abortTransaction(): 回滚事务,撤销所有操作
endSession(): 结束会话

3. 参数解析

readConcern: 定义事务的读取一致性级别,常见的级别有:

  • local:默认级别,读取最近的数据
  • majority:读取已经被大多数副本集成员确认的数据
  • linearizable:保证读取的结果是最新的,但可能会影响性能

writeConcern: 定义事务的写入确认级别,常见的级别有:

  • w: 1:仅确认写入到主节点
  • w: “majority”:确认写入到大多数节点
session.startTransaction({
    readConcern: { level: 'majority' },
    writeConcern: { w: 'majority' }
});

maxTimeMS: 设置事务的超时时间

session.startTransaction({
    maxTimeMS: 60000 // 60});

4. 使用技巧

选择合适的事务级别

短事务: 尽量缩短事务的持续时间,以减少锁定时间和资源占用,对于长时间运行的事务,系统的性能可能会受到显著影响
批量操作: 将多个操作组合到一个事务中,避免在事务中进行过多的操作,大规模的事务可能导致性能瓶颈和资源竞争

监控事务

事务监控: 使用 MongoDB 的监控工具来观察事务的状态和性能,工具如 mongostat 和 MongoDB Atlas 提供的监控功能可以帮助识别潜在的事务问题

mongostat --port 27017

处理错误和回滚

错误处理: 确保在事务中处理可能出现的错误,使用 try-catch 块来捕获异常并回滚事务
事务超时: 设置合适的 maxTimeMS,以避免长时间的事务锁定系统资源

跨库事务

跨数据库事务: MongoDB 支持跨数据库的事务,但可能会影响性能,确保在设计数据模型时,尽量避免跨数据库事务的需求

// 开启跨数据库事务
session.startTransaction({
    readConcern: { level: 'majority' },
    writeConcern: { w: 'majority' }
});

// 跨数据库操作
await client.db('db1').collection('coll1').insertOne({ a: 1 }, { session });
await client.db('db2').collection('coll2').insertOne({ b: 2 }, { session });

// 提交事务
await session.commitTransaction();

5. 故障处理

事务失败: 在处理事务时,确保能够处理事务失败的情况,回滚事务并记录错误信息,便于后续分析
重新尝试: 在遇到事务失败时,考虑实现重试逻辑,以便在出现临时性问题时恢复操作

数据恢复与故障恢复

MongoDB支持日志记录和存储引擎级别的数据恢复,并且当主节点发生故障时,复制集中的其他副本可以快速提升为主节点,保证数据持续可用

1. 数据恢复机制

1.1 备份与恢复

备份是确保数据安全性的重要手段,而在 MongoDB 中,常见的备份和恢复方式有:
逻辑备份: 使用 mongodump 和 mongorestore 工具导出和导入数据
物理备份: 直接复制数据库文件,可以使用文件系统层面的备份工具

逻辑备份示例
备份数据: 使用 mongodump 工具创建逻辑备份

mongodump --db mydatabase --out /path/to/backup

–db: 指定要备份的数据库
–out: 指定备份输出目录
恢复数据: 使用 mongorestore 恢复逻辑备份

mongorestore --db mydatabase /path/to/backup/mydatabase
  • –db:指定要恢复到的数据库
  • 后面的路径为备份存储的位置

参数解析

参数说明
–db要备份或恢复的数据库名称
–out备份文件的输出目录
–gzip支持对备份进行 gzip 压缩
–drop恢复时删除目标集合,如果集合已存在

1.2 使用复制集进行数据恢复

MongoDB 的复制集功能提供了高可用性和数据冗余,可以在主节点失败时自动故障转移到从节点
设置复制集: 在 MongoDB 中配置复制集,提高数据的可用性

rs.initiate();
rs.add("mongodb1:27017");
rs.add("mongodb2:27017");

故障转移: 当主节点宕机时,从节点会自动选举新的主节点

2. 故障恢复机制

故障恢复是指在发生系统故障时,能够有效地恢复到一定的服务状态

2.1 使用 WiredTiger 存储引擎的恢复特性

MongoDB 的 WiredTiger 存储引擎提供了良好的故障恢复能力,支持快速恢复和写操作的持久性
数据提交: WiredTiger 会在每次数据修改时记录操作,这确保了在系统崩溃时不会丢失最近的操作

2.2 进行“轻量级”恢复

当遇到写节点故障时,可以通过从节点的 oplog 进行恢复
查看 oplog: 可以查看 oplog 中的操作记录

use local;
db.oplog.rs.find().limit(10);

手动回滚操作: 在从节点上手动恢复未完成的数据状态

参数解析

参数说明
use local切换到 local 数据库,查看 oplog
find()查询 oplog 中的操作记录

3. 恢复策略与使用技巧

3.1 定期备份

安排定期备份: 根据数据更新频率和业务需求,定期使用 mongodump 创建逻辑备份
增量备份: 根据应用需求,结合 mongodump 和 mongorestore 的 --gzip 参数进行增量备份

3.2 监控复制集状态

使用 rs.status(): 监控复制集的状态,确保副本集中的所有节点正常运行

rs.status();

设置告警: 针对节点宕机或数据复制延迟设置告警,确保能够及时处理故障

3.3 故障测试

模拟故障: 定期进行故障模拟测试,以验证系统恢复能力
测试备份恢复过程: 在非生产环境中测试备份和恢复流程,确保在真实的故障发生时能够快速恢复

4. 使用mongod 提供的恢复选项

在启动 mongod 实例时,可以使用一些参数来进行恢复
–repair: 尝试修复 MongoDB 数据文件,但仅在特定情况下使用

mongod --repair --dbpath /path/to/data

优化查询性能

包括使用索引、适当使用$index hint、调整分片策略等,以减少磁盘读写和提高查询效率

1. 索引优化

1.1 创建索引

- 创建单字段索引

db.collection.createIndex({ field: 1 });
  • field:要索引的字段名称
  • 1:表示升序索引,-1 表示降序索引

创建复合索引

db.collection.createIndex({ field1: 1, field2: -1 });
  • field1 和 field2:要索引的字段,索引将按 field1 升序,field2 降序排序

创建唯一索引

db.collection.createIndex({ field: 1 }, { unique: true });
  • unique: true:确保索引字段的值唯一

1.2 使用 explain() 方法

查看查询计划

db.collection.find({ field: value }).explain("executionStats");
  • explain(“executionStats”):提供详细的执行统计信息,帮助分析查询性能

参数解析

参数说明
field要创建索引的字段名称
1 或 -1索引的排序方式,1 为升序,-1 为降序
unique如果设置为 true,则索引字段的值必须唯一

1.3 移除不必要的索引

查看所有索引

db.collection.getIndexes();

删除索引

db.collection.dropIndex("indexName");
  • indexName:要删除的索引名称

2. 查询优化

2.1 使用合适的查询条件

利用索引字段进行查询

db.collection.find({ indexedField: value });

确保查询条件中的字段是索引字段,以充分利用索引加速查询

避免全表扫描
尽量避免不使用索引的查询,例如 find() 查询中没有字段条件的情况

2.2 使用投影来减少返回的数据量

  • 只返回需要的字段
db.collection.find({ field: value }, { projection: { field1: 1, field2: 1 } });
  • projection:指定返回的字段,1 表示包含,0 表示排除

3. 集合和文档设计优化

3.1 规范化与反规范化

规范化: 将重复的数据分离到不同的集合中,以减少数据冗余,例如,将用户信息和订单信息分别存储在两个集合中
反规范化: 将相关数据嵌入到同一个文档中,以减少查询次数,例如,将订单项嵌入到订单文档中

// 嵌套文档示例
{
    _id: 1,
    orderItems: [
        { itemId: 101, quantity: 2 },
        { itemId: 102, quantity: 1 }
    ]
}

3.2 使用合理的数据类型

选择合适的数据类型: 选择数据类型时应考虑存储效率和查询性能,例如,使用 int 替代 string 存储整数类型的字段

4. 聚合管道优化

4.1 使用索引优化 $match 阶段

  • 在 $match 阶段使用索引字段
db.collection.aggregate([
    { $match: { indexedField: value } },
    { $group: { _id: "$groupField", total: { $sum: "$amount" } } }
]);

确保 $match 阶段使用索引字段,以提高性能

4.2 限制处理的数据量

  • 使用 $limit和 $skip 限制结果集
db.collection.aggregate([
    { $match: { field: value } },
    { $limit: 100 }
]);
  • $limit:限制返回的文档数量
  • $skip:跳过指定数量的文档,用于分页查询

5. 内存和缓存优化

5.1 调整 WiredTiger 缓存设置

  • 调整 storage.wiredTiger.engineConfig.cacheSizeGB
storage:
  wiredTiger:
    engineConfig:
      cacheSizeGB: 2
  • cacheSizeGB:设置 WiredTiger 存储引擎的缓存大小

5.2 配置适当的 maxTimeMS 限制

- 设置查询超时

db.collection.find({ field: value }).maxTimeMS(5000);
  • maxTimeMS:限制查询的最大执行时间,单位为毫秒

6. 使用 Sharding 提升性能

6.1 配置 Shard Key

  • 选择合适的 Shard Key
db.collection.createIndex({ shardKey: 1 });
db.adminCommand({ shardCollection: "mydb.collection", key: { shardKey: 1 } });
  • shardKey:选择用于分片的字段

6.2 监控和调整 Shard 配置

  • 查看分片状态
db.adminCommand({ listShards: 1 });
  • listShards:列出所有的分片信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值