MongoDB 索引与事务

索引与优化

1.1 索引类型

类型 说明
单字段索引 最基础,{field: 1} / {field: -1}
复合索引 多字段组合,遵循最左前缀
多键索引(Multikey) 字段是数组时自动创建,每个数组元素一条索引项
唯一索引 {unique: true}_id 默认唯一且不可删
稀疏索引 只索引「存在该字段」的文档
部分索引 partialFilterExpression 只索引满足条件的文档,比稀疏更灵活
TTL 索引 基于 Date 字段到期自动删除
文本索引 {field: "text"},支持全文检索
地理空间索引 2dsphere / 2d
哈希索引 {field: "hashed"},用于哈希分片
每个集合默认在 _id 字段上有一个唯一索引。
javascript 复制代码
//  个人最常用的是单字段索引、复合索引、后台建立索引、查询集合索引
db.user.createIndex({ name: 1 })
db.user.createIndex({ name: 1, age: -1 })                                   // 复合
db.user.createIndex({ name: 1 }, { unique: true })                           // 唯一
db.user.createIndex({ email: 1 }, { unique: true, sparse: true })            // 稀疏
db.user.createIndex({ age: 1 }, { partialFilterExpression: { age: { $gt: 18 } } }) // 部分
db.user.createIndex({ createTime: 1 }, { expireAfterSeconds: 3600 })         // TTL
db.user.createIndex({ desc: "text" })                                        // 文本
db.user.createIndex({ loc: "2dsphere" })                                     // 地理
db.user.createIndex({ shardKey: "hashed" })                                  // 哈希
db.users.createIndex({ age: 1 }, { background: true }) // 后台建立索引(避免生产环境读写被阻塞)
db.user.getIndexes()  
db.user.dropIndex("name_1")

1.2 索引核心规则

问题 答题要点
最左前缀原则 查询条件必须包含复合索引的前导字段,否则用不上
ESR 规则 复合索引字段顺序按 Equality(等值查询) → Sort(排序查询) → Range(范围查询) 排列
覆盖索引 查询条件 + 返回字段全在索引里,不用回表;explain 结果里没有 FETCH 阶段 ;注意排除 _id(投影加 _id: 0
索引失效场景 $ne / $nin / $not$regex^ 前缀;类型不一致(用字符串查数字);跳过复合索引前导字段;$or 中有字段没索引
索引的代价 占空间 + 写放大(每次写都要维护索引),不是越多越好

1.3 与 MySQL 索引对比

维度 MySQL(InnoDB) MongoDB
索引结构 B+树 B树
索引类型 主键、唯一、普通、联合、全文等 单字段、复合、多键、文本、地理、哈希、TTL
最左前缀 支持 支持(复合索引)
索引覆盖 支持(覆盖索引) 支持(covered query)
索引代价 占用空间,降低写入速度 同上

1.4 慢查询定位

javascript 复制代码
// 有阿里云服务器的,可查询MongoDB慢SQL日志
// 1. 开启慢查询采集(慢于 100ms 记录到 system.profile)
db.setProfilingLevel(1, { slowms: 100 })
db.system.profile.find().sort({ ts: -1 }).limit(10)

// 2. 执行计划分析
db.user.find({ age: { $gt: 18 } }).explain("executionStats")

看三个关键值:

  • stageCOLLSCAN 全表扫描(要警惕) / IXSCAN 走索引(正常)
  • totalDocsExaminednReturned:比值应接近 1,差得越多说明索引选择性越差
  • executionTimeMillis:实际耗时

辅助工具:mongostat(整体吞吐)、mongotop(各集合读写耗时)、db.currentOp()(当前操作)。


1.5 TTL / 稀疏 / 部分索引的坑

  • TTL 索引基于 Date 字段 + expireAfterSeconds;后台线程每 60 秒扫一次,删除时间不精确;被 TTL 删除的文档同样会产生 oplog
  • 稀疏索引只跳过「完全没有该字段」的文档,字段值为 null 仍会被索引
  • 部分索引用 partialFilterExpression,表达力更强,优先选部分索引

1.6 性能优化

  1. 为高频查询字段建索引 ,尤其是 wheresortjoin 用到的字段。
  2. 复合索引字段顺序:等值查询在前,范围查询在后,排序字段最后。
  3. 避免过多索引:每个索引都会降低写入速度,增加存储。
  4. 使用覆盖查询:尽量让查询字段和返回字段都包含在索引中。
  5. 使用部分索引:只索引常用子集,减少索引大小。
  6. 使用 TTL 索引:自动清理过期数据。
  7. 定期分析慢查询 :开启 profiling,用 explain 诊断。
  8. 避免在索引字段上使用 $ne$not$regex(非前缀),这些通常导致全表扫描。

事务与一致性

一、事务基础概念

1.1 什么是事务

事务是一组操作的集合,这些操作要么全部成功,要么全部失败,保证数据的一致性。

1.2 MongoDB 事务的发展

版本 支持情况
4.0 副本集支持多文档事务
4.2 分片集群支持多文档事务
4.4 支持在事务中创建集合和索引
5.0 事务性能优化,支持更多场景

1.3 与关系型数据库的对比

维度 MySQL MongoDB
事务粒度 行级、表级 文档级、多文档
隔离级别 读未提交、读已提交、可重复读(默认)、串行化 快照隔离(Snapshot Isolation)
默认隔离级别 可重复读 快照隔离
跨分片事务 支持 4.2+ 支持
性能开销 较高 较高,但通常低于跨分片
使用复杂度 简单、SQL标准 需要Session
适用场景 强事务、复杂关联 灵活文档、水平扩展、事务需求少
java 复制代码
const session = db.getMongo().startSession();
session.startTransaction();
try {
  session.getDatabase("app").orders.updateOne({ _id: 1 }, { $set: { status: "PAID" } });
  session.getDatabase("app").stock.updateOne({ sku: "A" }, { $inc: { qty: -1 } });
  session.commitTransaction();
} catch (e) {
  session.abortTransaction();
}

二、单文档原子性

2.1 核心原则

单文档原子性 :MongoDB 对单个文档的更新天然原子(含嵌套子文档、数组),很多「伪事务」场景其实一条 updateOne 就能解决,不必上事务。

2.3 与多文档事务的关系

  • 单文档原子性不需要显式事务,MongoDB 自动保证。
  • 多文档事务只在需要跨文档、跨集合、跨分片时使用。

三、多文档事务

3.1 使用场景

  • 跨集合操作(如订单和库存)
  • 跨文档操作(如转账)
  • 跨分片操作(分片集群)

3.2 基本语法

javascript 复制代码
const session = db.getMongo().startSession();
session.startTransaction({
  readConcern: { level: "snapshot" },
  writeConcern: { w: "majority" }
});

try {
  const orders = session.getDatabase("shop").orders;
  const inventory = session.getDatabase("shop").inventory;

  orders.updateOne({ _id: 1 }, { $set: { status: "confirmed" } });
  inventory.updateOne({ sku: "A001" }, { $inc: { stock: -1 } });

  session.commitTransaction();
} catch (error) {
  session.abortTransaction();
  throw error;
} finally {
  session.endSession();
}

3.3 读关注(readConcern)

决定读取操作 能看到什么样的数据

控制读的时候,数据需要被多少个节点确认,或者是否需要一致性快照

级别 说明
local 默认,读取当前节点最新数据,可能回滚
majority 读取已确认多数节点的数据,不会回滚
snapshot 事务内使用,保证一致性快照
linearizable 线性一致,保证读到最新已提交数据

3.4 写关注(writeConcern)

写操作 需要被确认到什么程度,才返回成功

控制写操作 要写到多少个节点、是否要落盘,才算成功

级别 说明 适用场景
w: 1 主节点确认即可 一般写入,性能优先
w: majority 多数节点确认,推荐用于事务 重要数据,防止回滚
w: 0 不等待确认,直接返回 日志、监控等可容忍丢失场景
j: true 写入 journal 后才确认 需持久化保证
w: majority + j: true 多数节点确认且落盘 最高持久性,性能最差

四、ACID 特性

特性 MongoDB 支持情况
原子性(Atomicity) 单文档天然原子;多文档事务保证全部成功或全部失败
一致性(Consistency) 事务提交后数据满足约束;应用层需自行保证业务规则
隔离性(Isolation) 快照隔离,事务之间互不干扰;写冲突时事务中止并重试
持久性(Durability) 依赖 writeConcern,w:majority + j:true 可保证

五、使用限制与注意事项

5.1 限制

  • 事务超时 :默认 60 秒,可调整 transactionLifetimeLimitSeconds
  • 文档大小:事务中所有操作的总大小不能超过 16MB(oplog 限制)。
  • 不支持的操作
    • 创建或删除集合(4.4 之前)
    • 创建或删除索引(4.4 之前)
    • count 命令(可使用 countDocuments
    • distinct 命令
    • 某些管理命令
  • 分片集群:跨分片事务性能开销大,需谨慎使用。
  • 写冲突:事务内写冲突会导致事务中止,应用需重试。

5.2 性能开销

  • 多文档事务比单文档操作慢,因为需要协调多个节点、维护快照、写 oplog。
  • 事务越长,持有锁和资源越久,冲突概率越高。
  • 建议事务尽量短小,避免长时间运行。

5.3 重试机制

javascript 复制代码
// 事务因写冲突中止时,应用层应重试
function runTransactionWithRetry(session, txnFunc) {
  while (true) {
    try {
      session.startTransaction();
      txnFunc(session);
      session.commitTransaction();
      break;
    } catch (error) {
      session.abortTransaction();
      if (error.hasErrorLabel("TransientTransactionError")) {
        continue; // 重试
      }
      throw error;
    }
  }
}
相关推荐
SelectDB1 小时前
Doris vs ClickHouse:企业 OLAP 走向下一阶段,两种技术路线如何选择
大数据·数据库·数据分析
vx-程序开发1 小时前
【计算机毕设】django校园跑腿服务系统83141
java·数据库·vue.js·spring boot·spring·elasticsearch·django
&不羁之风&1 小时前
从 MySQL 到 MongoDB:80 万系统日志(sys_log)平滑迁移完整实战(含 Windows 环境 mongoimport 安装与避坑)
mysql·mongodb
勇气要爆发1 小时前
006.注解
java·注解
liuchangng1 小时前
Jev 模型研究:从生成式大模型到决策式模型——System One、RLCD 校准与采用边界
java·javascript·人工智能·python·深度学习
GPU实战笔记1 小时前
本地跑不动 llama.cpp,临时租云 GPU 怎么搭?从启动服务到环境复用
java·服务器·网络·人工智能·深度学习·llama
撸串-研究生1 小时前
SSM + Vue 线上作业批改系统:在线答题、文件提交与教师批改闭环90608
java·ssm·在线答题·作业批改·文件提交
一 乐2 小时前
养老院管理系统|基于springboot + vue养老院管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
wang_shu_mo_ran2 小时前
Spring MVC 常用注解和用法(二)——url,cookie,session
java·mvc