跳至主要内容

MongoDB快速入门:MySQL用户指南

MongoDB 从入门到精通

—— 写给有 MySQL 基础的你


目录

  1. 先建立心智模型:术语对照表
  2. 存储引擎的本质区别
  3. 数据结构的本质区别:BSON 与文档模型
  4. 安装与连接
  5. CRUD 操作对照速查
  6. 查询操作符大全
  7. 索引
  8. 聚合框架(替代 JOIN / GROUP BY)
  9. 数据建模:内嵌 vs 引用
  10. 事务与一致性
  11. 高可用与扩展:副本集与分片
  12. 性能优化与最佳实践
  13. 常用工具
  14. 学习路径建议

1. 先建立心智模型:术语对照表

MySQL 的知识不会浪费,几乎每个概念在 MongoDB 里都有对应物,只是实现方式不同。

MySQL(关系型) MongoDB(文档型) 说明
Database Database 概念一致
Table Collection(集合) 一组文档的容器,无固定 schema
Row(行) Document(文档) 一条 BSON 格式的记录
Column(列) Field(字段) 文档内的键值对
Primary Key _id 字段 MongoDB 自动生成 ObjectId 作为主键
Index Index 概念一致,用法高度相似
JOIN $lookup(聚合阶段)或应用层多次查询 MongoDB 更推荐"反范式内嵌"来避免 JOIN
Schema(强约束) Schema-less / 动态 Schema 同一集合里的文档字段可以不同(但不代表不需要设计
GROUP BY Aggregation Pipeline 的 $group 表达能力更强,是"流水线"式处理
事务(InnoDB 默认支持) 多文档事务(4.0+ 支持,但设计上应尽量避免依赖) 理念不同:MongoDB 鼓励原子性的单文档操作
外键约束 无原生外键(应用层保证一致性) 这是最大的思维转变点之一
mysql CLI mongosh 官方交互式 Shell,语法是 JavaScript

关键心态转变:MySQL 设计时先想"如何范式化、避免冗余";MongoDB 设计时先想"这个数据在应用中是如何被读取的,如何组织能一次查询就拿到"。这是贯穿全文最重要的一条原则。


2. 存储引擎的本质区别

2.1 MySQL:InnoDB

  • 数据结构:聚簇索引(Clustered Index),基于 B+ 树,主键即数据的物理排序依据;二级索引存储主键值作为指针,回表查询主键索引获取完整行。
  • 事务:原生支持 ACID,通过 Undo Log(回滚)+ Redo Log(持久化)+ MVCC 实现隔离级别(默认 REPEATABLE READ)。
  • :行级锁 + 间隙锁(Gap Lock),用于防止幻读。
  • 存储粒度:以"行"为单位,表结构(schema)在创建表时固定,修改需要 ALTER TABLE(大表可能锁表或走 Online DDL)。

2.2 MongoDB:WiredTiger(自 3.2 起默认引擎)

  • 数据结构:同样基于 B 树(不是 B+ 树),但存储的是整份 BSON 文档而非拆分的行列。
  • 并发控制:文档级别(document-level)的乐观并发控制,配合 MVCC——这比 MySQL 的行级锁粒度更细,写入吞吐通常更高。
  • 压缩:默认对数据启用 snappy 压缩,也可选 zstd/zlib,这是 MySQL InnoDB 默认不做的(需手动开启页压缩)。
  • 持久化:Journal(预写日志,类似 Redo Log)+ Checkpoint(约每 60 秒做一次快照落盘),两者共同保证崩溃恢复。
  • 历史引擎:早期版本默认是 MMAPv1(内存映射文件,表级锁,性能较差),从 3.2 版本起 WiredTiger 成为默认并集合社区不再使用 MMAPv1,现代 MongoDB(4.2+)已完全移除 MMAPv1。如果你看到很老的教程提到"MongoDB 表锁很严重",说的就是 MMAPv1 时代的旧闻。
  • 可插拔架构:MongoDB 的存储引擎是可插拔的(Storage Engine API),WiredTiger 是官方主推的通用引擎,另外还有面向内存计算场景的 In-Memory 引擎(企业版)。

2.3 对比小结

维度 InnoDB WiredTiger
索引结构 B+ 树,聚簇索引 B 树,文档存储
并发粒度 行级锁 文档级 MVCC
默认压缩 无(需手动开) 有(snappy)
Schema 变更成本 可能较高(视场景) 几乎为零(应用层控制)
事务模型 原生、深度优化 支持但非核心设计理念

3. 数据结构的本质区别:BSON 与文档模型

3.1 什么是 BSON

MongoDB 存储的不是 JSON 文本,而是 BSON(Binary JSON)——一种二进制编码格式,在 JSON 基础上增加了更多数据类型,并且便于机器快速解析(不需要像文本 JSON 那样做字符串扫描)。

常见 BSON 类型对比 MySQL 类型:

MySQL 类型 BSON / MongoDB 类型
INT / BIGINT Int32 / Int64 (NumberLong)
DECIMAL Decimal128(精确十进制,避免浮点误差)
VARCHAR / TEXT String(UTF-8)
DATETIME Date(内部存储为 UTC 毫秒时间戳)
BOOLEAN Boolean
BLOB BinData
自增主键 ObjectId(12 字节,含时间戳+机器标识+计数器,全局唯一,不需要中心化分配)
JSON 类型列 原生支持嵌套 Object / Array,这是一等公民,不是"额外类型"
无对应 Array(数组作为字段值,可直接建索引——"多键索引")

3.2 一个直观例子

MySQL:一个订单需要 3 张表

-- orders 表
CREATE TABLE orders (id INT PRIMARY KEY, user_id INT, created_at DATETIME);
-- order_items 表
CREATE TABLE order_items (id INT PRIMARY KEY, order_id INT, product_name VARCHAR(100), qty INT);
-- 查询一个订单及其明细需要 JOIN
SELECT o.*, i.product_name, i.qty
FROM orders o JOIN order_items i ON o.id = i.order_id
WHERE o.id = 1001;

MongoDB:一个文档就装下整个订单

{
  _id: ObjectId("64f1a2..."),
  userId: 2001,
  createdAt: ISODate("2026-09-01T10:00:00Z"),
  items: [
    { productName: "机械键盘", qty: 1, price: 399 },
    { productName: "鼠标垫", qty: 2, price: 29 }
  ],
  status: "shipped"
}

一次 findOne 就能拿到订单和明细,无需 JOIN。这就是文档模型的核心优势:把"经常一起被读取的数据"物理上存在一起。

3.3 动态 Schema 不等于"没有设计"

很多初学者误解"schema-less"为"随便存",这是常见的坑。实践中通常会:

  • Schema Validation$jsonSchema)在集合层面约束必填字段和类型,相当于"软性的表结构"。
  • 用应用层(如 Mongoose、Prisma)定义模型来约束一致性。
db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email"],
      properties: {
        name: { bsonType: "string" },
        email: { bsonType: "string", pattern: "^.+@.+$" },
        age: { bsonType: "int", minimum: 0 }
      }
    }
  }
});

4. 安装与连接

4.1 安装(以 Ubuntu 为例,本地开发环境)

# 导入官方 GPG key 并添加源(略,参考 mongodb 官网当前版本命令)
sudo apt-get install -y mongodb-org
sudo systemctl start mongod
sudo systemctl enable mongod

也可以直接使用官方托管服务 MongoDB Atlas(提供免费额度),跳过本地安装,类似于云数据库 RDS 之于 MySQL。

4.2 连接:mongosh

mongosh "mongodb://localhost:27017"
# Atlas 云端示例
mongosh "mongodb+srv://user:password@cluster0.xxxxx.mongodb.net/mydb"

mongosh 本质是一个 Node.js 环境,语法就是 JavaScript,这一点与 mysql CLI(纯 SQL)不同——你可以直接写变量、循环、函数。

use mydb                    // 相当于 MySQL 的 USE mydb;
show collections            // 相当于 SHOW TABLES;
db.users.find().limit(5)    // 查询

5. CRUD 操作对照速查

5.1 增(Create)

-- MySQL
INSERT INTO users (name, age) VALUES ('Tom', 25);
// MongoDB
db.users.insertOne({ name: "Tom", age: 25 });
db.users.insertMany([{ name: "A" }, { name: "B" }]); // 批量

5.2 查(Read)

-- MySQL
SELECT name, age FROM users WHERE age > 20 ORDER BY age DESC LIMIT 10;
// MongoDB
db.users.find(
  { age: { $gt: 20 } },        // 查询条件,等价于 WHERE
  { name: 1, age: 1, _id: 0 }  // 投影,等价于 SELECT 字段
).sort({ age: -1 }).limit(10);

5.3 改(Update)

-- MySQL
UPDATE users SET age = 26 WHERE name = 'Tom';
// MongoDB
db.users.updateOne(
  { name: "Tom" },
  { $set: { age: 26 } }
);
// 批量更新用 updateMany;$inc 自增、$push 向数组追加元素等
db.users.updateMany({}, { $inc: { loginCount: 1 } });

⚠️ 重要区别:如果忘记加 $set 直接写 { age: 26 },MongoDB 会把整个文档替换{ age: 26 },而不是"更新这一个字段"!这是新手最容易踩的坑之一。

5.4 删(Delete)

-- MySQL
DELETE FROM users WHERE age < 18;
// MongoDB
db.users.deleteMany({ age: { $lt: 18 } });
db.users.deleteOne({ name: "Tom" }); // 只删第一条匹配

6. 查询操作符大全

SQL MongoDB 操作符 示例
= 直接写值 { age: 25 }
> / >= $gt / $gte { age: { $gt: 20 } }
< / <= $lt / $lte { age: { $lt: 60 } }
!= $ne { status: { $ne: "closed" } }
IN (...) $in { status: { $in: ["a","b"] } }
NOT IN $nin { status: { $nin: ["a"] } }
LIKE '%abc%' $regex { name: { $regex: "abc", $options: "i" } }
AND 默认(多字段自动 AND)或 $and { age: { $gt: 20 }, city: "SG" }
OR $or { $or: [{ age: 20 }, { city: "SG" }] }
IS NULL $eq: null$exists: false { deletedAt: null }
数组包含某元素 $elemMatch / 直接写值 { tags: "vip" }(数组字段自动匹配任一元素)

7. 索引

索引的思维方式和 MySQL 高度一致:都是为了避免全表扫描(COLLSCAN,相当于 MySQL 的 ALL 全表扫描),本质也是树结构。

// 单字段索引
db.users.createIndex({ age: 1 });          // 1 升序,-1 降序

// 复合索引(顺序很重要,和 MySQL 最左前缀原则完全一致)
db.orders.createIndex({ userId: 1, createdAt: -1 });

// 唯一索引
db.users.createIndex({ email: 1 }, { unique: true });

// 多键索引:数组字段可以直接建索引,MySQL 没有直接对应物
db.articles.createIndex({ tags: 1 });

// TTL 索引:文档到期自动删除,常用于日志/会话数据,MySQL 需要定时任务模拟
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 });

// 文本索引,用于简单全文检索
db.articles.createIndex({ content: "text" });

分析查询计划(对应 MySQL 的 EXPLAIN):

db.users.find({ age: { $gt: 20 } }).explain("executionStats");
// 关注 winningPlan.stage 是 IXSCAN(走索引)还是 COLLSCAN(全表扫描)

8. 聚合框架(替代 JOIN / GROUP BY)

MongoDB 的 Aggregation Pipeline 是数据依次流经一系列"处理阶段"(stage),类似 Unix 管道 |,表达能力比 SQL 的 GROUP BY 更强、更灵活。

8.1 GROUP BY 对照

-- MySQL
SELECT city, COUNT(*) AS cnt, AVG(age) AS avg_age
FROM users
GROUP BY city
HAVING cnt > 5
ORDER BY cnt DESC;
// MongoDB
db.users.aggregate([
  { $group: { _id: "$city", cnt: { $sum: 1 }, avgAge: { $avg: "$age" } } },
  { $match: { cnt: { $gt: 5 } } },   // 相当于 HAVING
  { $sort: { cnt: -1 } }
]);

8.2 JOIN 对照($lookup

-- MySQL
SELECT o.*, u.name
FROM orders o JOIN users u ON o.user_id = u.id;
// MongoDB
db.orders.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "userInfo"
    }
  },
  { $unwind: "$userInfo" } // 把数组展开成单个对象,类似 INNER JOIN 效果
]);

性能提示$lookup 是可用的逃生舱,但不是首选方案。能用内嵌解决的关联,优先内嵌;真正需要多对多、或关联数据经常独立变化时才用 $lookup

8.3 常用阶段速查

阶段 作用 SQL 类比
$match 过滤文档 WHERE
$project 选择/计算字段 SELECT
$group 分组聚合 GROUP BY
$sort 排序 ORDER BY
$limit / $skip 分页 LIMIT / OFFSET
$lookup 关联其他集合 JOIN
$unwind 展开数组为多条文档 无直接对应,类似"炸开"一对多关系
$facet 一次执行多条并行的子聚合 无直接对应,很强大

9. 数据建模:内嵌 vs 引用

这是从 MySQL 转到 MongoDB 最重要的设计决策,没有"正确答案",只有"更适合当前读写模式的答案"。

9.1 决策参考表

关系类型 建议 原因
一对少量(1:N,N 较小,如"文章-评论(<100条)") 内嵌 一次查询拿全部数据,避免 JOIN
一对海量(1:N,N 可能无限增长,如"用户-日志") 引用(外部集合存主表 _id 内嵌会导致单文档无限膨胀,超过 16MB 文档大小上限
多对多 引用 + 中间集合,或双向引用数组 类似 MySQL 的关联表,但更灵活
数据是否经常单独更新 若关联数据独立变化频繁,倾向引用 避免内嵌数据频繁重复更新导致写放大
数据是否需要跨文档强一致读取 需要则引用 + 事务,否则内嵌更简单 内嵌天然保证同一文档内的原子性

9.2 常见 Schema 设计模式(选读,进阶)

  • Subset Pattern(子集模式):只在主文档内嵌"最常访问的部分"数据(如商品的前 10 条评论),其余存在独立集合,兼顾读性能和文档体积。
  • Bucket Pattern(桶模式):如 IoT 时序数据,把同一小时的多条读数打包进一个"桶"文档,减少文档数量、提高写入和聚合效率。
  • Extended Reference Pattern:引用其他集合时,把常用的几个字段"顺便冗余一份"过来(如订单里冗余存一份用户名),减少 $lookup 次数,代价是需要处理冗余字段的更新同步。

16MB 限制提醒:单个 BSON 文档上限是 16MB,这是 MySQL 没有的硬约束,设计无限增长的内嵌数组(如"给用户加好友")时要格外注意,通常改为引用或分桶。


10. 事务与一致性

  • 单文档操作天然原子:MongoDB 对单个文档的任何更新(哪怕更新多个字段/数组元素)都是原子的,这也是为什么好的 Schema 设计会尽量把"需要一起变化的数据"放进同一文档——从而根本不需要事务。
  • 多文档事务:自 4.0(副本集)/ 4.2(分片集群)起原生支持类似 MySQL 的多语句事务:
const session = db.getMongo().startSession();
session.startTransaction();
try {
  session.getDatabase("mydb").accounts.updateOne({ _id: 1 }, { $inc: { balance: -100 } });
  session.getDatabase("mydb").accounts.updateOne({ _id: 2 }, { $inc: { balance: 100 } });
  session.commitTransaction();
} catch (e) {
  session.abortTransaction();
}
  • Read/Write Concern:这是 MongoDB 特有、MySQL 没有直接对应的一套可调一致性/持久性级别机制(因为 MongoDB 天生是分布式的):
    • writeConcern: { w: "majority" } —— 写入需要被大多数副本节点确认才算成功,类似"半同步复制"的加强版。
    • readConcern: "majority" —— 只读取已被多数节点确认的数据,避免读到可能被回滚的数据。
    • 这一套机制在 MySQL 主从复制里没有细粒度的直接对照,可以类比为"你自己可以调节 CAP 中一致性和可用性的取舍旋钮"。

实践建议:不要把 MongoDB 当成"可以到处用事务的关系型数据库",事务是补救手段,不是设计起点;先想清楚建模,事务需求会大幅减少。


11. 高可用与扩展:副本集与分片

11.1 副本集(Replica Set)对比主从复制

MySQL 主从复制 MongoDB 副本集
一主多从,手动/半自动切换(需 MHA/Orchestrator 等工具) 一主(Primary)多从(Secondary),原生自动故障转移(Failover),无需额外组件
从库默认异步复制 默认也是异步,但可通过 Write Concern 调整为近似同步
读写分离需应用层/中间件路由 驱动原生支持 readPreference(如 secondaryPreferred

11.2 分片(Sharding)对比分库分表

MySQL 分库分表 MongoDB Sharding
通常需要 ProxySQL/Vitess/中间件,业务侵入性较大 原生内置分片架构,对应用基本透明
分片键需手工设计路由逻辑 需选择 Shard Key,之后 mongos 路由层自动处理请求分发
扩容需要复杂的数据迁移方案 支持 chunk 自动在分片间迁移均衡(Balancer)

Shard Key 的选择原则和 MySQL 分库键设计思路一致:需要高基数、写入均匀分布、尽量匹配常用查询条件,选不好一样会出现热点分片问题。


12. 性能优化与最佳实践

  1. 像 MySQL 一样重视索引:没有命中索引的查询在数据量大时同样会拖垮 MongoDB,explain() 要养成习惯用。
  2. 投影只取需要的字段find(query, { field: 1 }),减少网络和内存开销,等价于不要 SELECT *
  3. 警惕无界数组增长:数组字段和索引都要考虑上限,16MB 文档限制、以及索引对超大数组的性能影响。
  4. 合理使用 $lookup,但不要滥用:多次 $lookup 相当于多次 JOIN,仍然有性能代价,能内嵌就内嵌。
  5. 连接池:MongoDB 驱动同样有连接池概念,生产环境要像调 MySQL 连接池一样调 maxPoolSize
  6. 监控工具mongostat(类似 mysqladmin status)、mongotop(看哪个集合读写最热)、Atlas 自带的 Performance Advisor 会自动建议缺失索引。
  7. 避免"关系型思维"陷阱:不要把每一个 MySQL 表原封不动建成一个 Collection 再疯狂 $lookup,那样等于用 MongoDB 的成本换 MySQL 的体验,还更差。

13. 常用工具

用途 MySQL 生态 MongoDB 生态
图形化管理 Workbench / Navicat MongoDB Compass(官方免费GUI)
云托管服务 RDS / Aurora MongoDB Atlas
ORM/ODM MyBatis / Sequelize Mongoose(Node.js)/ 各语言官方驱动
命令行 mysql mongosh
备份 mysqldump mongodump / mongorestore
导入导出 LOAD DATA / CSV mongoimport / mongoexport

14. 学习路径建议

第 1 周(入门) - 装好 MongoDB / 注册 Atlas 免费集群,跑通 mongosh 连接。 - 熟练 CRUD + 常用查询操作符(对照第 5、6 章反复练习)。

第 2 周(进阶) - 掌握索引设计与 explain() 分析(第 7 章)。 - 吃透聚合框架,能用 $group / $lookup 替代你原来写的复杂 SQL(第 8 章)。

第 3 周(建模思维转变,最关键) - 挑一个你熟悉的 MySQL 项目,尝试用"内嵌 vs 引用"重新设计一遍 Schema(第 9 章),这是拉开新手与熟练者差距的核心能力。 - 理解 16MB 限制、Bucket/Subset 等设计模式适用场景。

第 4 周(生产化) - 学习事务的正确使用边界(第 10 章)。 - 了解副本集和分片的原理,哪怕暂时用不到,也要知道它解决什么问题(第 11 章)。 - 过一遍性能优化清单(第 12 章),养成用 Compass 或 mongostat 观察系统状态的习惯。

之后(持续精进) - 官方文档(MongoDB Manual)是最权威、更新最及时的资料,配合实际项目反复实践 Schema 设计决策,才是从"会用"到"精通"的关键路径——这一点和数据库学习的规律其实和 MySQL 是一样的。


文档说明:本文基于 MongoDB 通用概念与 WiredTiger 存储引擎(现行默认引擎)编写,具体版本细节(如某些语法糖、Atlas 界面)可能随版本更新略有变化,建议关键操作以你所用版本的官方 Manual 为准。

此博客中的热门博文

Elasticsearch 读写原理指南

### 1. 什么是 segment,里面装了什么? 在 Lucene(也是 Elasticsearch)里,索引被切分成若干 **segment(段)**,每个 segment 是一个完整的、只读的倒排索引单元。一个 segment 包含: * **倒排词典** —— 用 **FST(Finite‑State Transducer)** 以高度压缩的形式保存每个字段出现的所有 term 以及 term→ord 的映射。对应的磁盘文件是 `*.tim`(新版)或 `*.tis/*.tii`(旧版)。 * **倒排列表(postings)** —— 保存每个 term 出现的文档 ID、频次、位置信息等,文件名通常是 `*.doc`、`*.pos`、`*.pay`。 * **存储字段**(_source、store:true 的字段)—— 以二进制块的形式写入 `*.fdt` / `*.fdx`。 * **doc‑values、norms、向量** 等辅助结构,分别保存在 `*.dv`、`*.norm`、`*.tv` 等文件里。 * **deleted‑docs bitmap**(`*.del`),标记哪些文档已被删除或被更新。 所有这些文件在 segment **写入磁盘后即成为只读**,后续的查询只能读取,永远不会在原文件上进行增删改。 --- ### 2. 原始文档和 FST 为什么都在 segment 里? * **原始文档**:Elasticsearch 默认把完整的 JSON(_source)以及任何 `store:true` 的字段写入 segment 的 `*.fdt/*.fdx` 文件。每个 segment 保存自己的那部分文档,旧的 segment 在合并前仍然保留,直到合并后被删除。 * **FST**:每个字段的词典在每个 segment 中单独维护,采用 FST 进行前缀共享和字节压缩。这样即使同一个 term 在多个 segment 中出现,也会在每个 segment 里拥有独立的映射,查询时只需要在对应 segment 的 FST 中定位即可。 --- ### 3. 查询时到底是怎么遍历 segment 的? 1. **请求入口**      客户端的搜索请求先到达 **协调节点**,协调节点把请求 ...

LLM缓存详解

 可以把“大模型缓存”理解成: 把已经算过的结果(或中间结果)存下来,下次尽量复用 。但这里面其实分几层,不只是简单的“问题→答案”缓存。 1️⃣ 常见的几种缓存类型 (1)KV Cache(推理内部缓存) Transformer 在生成时,会把前面 token 的 Key/Value 向量 缓存下来。 本质:避免重复计算 attention 作用: 同一请求内部加速 特点: 👉 只对“同一上下文继续生成”有效 👉 不跨用户、不跨请求 这类缓存是你体感“流式输出越来越快”的原因之一。 (2)Prompt Cache(提示词缓存) 缓存的是: 相同(或高度相似)的 prompt → 对应的中间表示 / 输出 典型场景: 系统提示词(system prompt)很长 多轮对话里前文基本不变 👉 这里能省掉 前缀计算成本(prefill) (3)Embedding / 语义缓存(Semantic Cache) 这个才是你问题的关键 👇 不是按“字符串完全一致”,而是: 把问题转成向量 → 找“语义相似”的历史问题 → 直接复用答案 2️⃣ 为什么命中缓存成本低很多? 因为大模型推理成本主要在两块: (1)Prefill(吃 prompt) 复杂度 ~ O(n²) 很贵(尤其长 prompt) (2)Decode(逐 token 生成) 每个 token 都要算一遍模型 而缓存命中后: KV cache:不用重复 attention Prompt cache:不用重新 encode 语义缓存: 直接跳过模型推理 👉 相当于从: 几十~几百毫秒 + GPU算力 变成: 一次向量检索(毫秒级)+ 直接返回 所以成本差一个数量级是正常的。 3️⃣ “每个人问法不同,怎么命中缓存?” 这是核心难点,也是工程重点👇 ❌ 不能靠字符串匹配 比如: “今天天气怎么样” “今天外面热不热” 字符串完全不同 → 必须 miss ✅ 用语义相似度(Embedding) 流程一般是: 把问题转 embedding(向量) 在向量数据库里找 TopK 相似问题 如果相似度 > 阈值(比如 0.9) 直接返回缓存答案 一个简单示意 Q1: 北京天气怎么样 → embedding A Q2: 北京今天热吗 → embedding B cosine(A, B) ≈ 0.95...

事务的ACID是什么

 事务的 ACID 是数据库事务必须满足的四个基本性质,用来保证在并发和故障情况下数据的正确性与可靠性: A(Atomicity,原子性) 一个事务中的操作要么 全部成功 ,要么 全部失败回滚 ,不存在“只做了一半”的中间状态。 C(Consistency,一致性) 事务执行前后,数据库都必须处于 一致的合法状态 ,满足约束(如主键、外键、唯一性、业务规则等)。 I(Isolation,隔离性) 并发执行的多个事务之间 相互隔离 ,一个事务未提交的中间结果对其他事务不可见(具体强弱由隔离级别决定)。 D(Durability,持久性) 一旦事务提交成功,其结果会被 永久保存 ,即使系统崩溃也不会丢失(通常依赖 WAL/redo log 等机制)。 一句话记忆: 要么全做完、前后不破坏规则、互不干扰、做完不丢。