数据库核心概念
无论你选择 MySQL、PostgreSQL 还是 MongoDB,数据库的底层逻辑是共通的。这篇文档把索引、事务、锁这三个最核心的概念讲透——不讲具体语法的细节,而是帮助你建立跨数据库的通用心智模型。
索引:数据库为什么要建索引?
一句话讲清楚
索引就是数据库的目录。没有索引,数据库查询就像从头翻一本没有目录的书——每一页都要看(全表扫描)。有了索引,数据库直接跳到对应章节(索引查找)。
为什么索引能加速查询?
索引在磁盘上是一个有序的数据结构(最常见的是 B-Tree)。查找一条记录,不需要遍历所有行,而是像查字典一样:先翻到中间,判断目标在左边还是右边,对半缩小范围——这就是二分查找,时间复杂度从 O(n) 降到 O(log n)。
有索引:查找 id=500 → 读 3~4 个索引页 → 定位 1 个数据页 → 完成
无索引:查找 id=500 → 扫描全部 10000 页 → 找到 id=500 → 完成索引不是免费的
索引加速了查询,但每次 INSERT / UPDATE / DELETE 时,索引也要同步更新。一张表上的索引越多,写入越慢。这就是为什么"不要给每个字段都建索引"——索引是时间换空间的权衡艺术。
关系型 vs 非关系型的索引差异
| 维度 | 关系型(MySQL/PG) | 非关系型(MongoDB) |
|---|---|---|
| 默认结构 | B-Tree | B-Tree |
| 独有类型 | PG 有 GIN/GiST/BRIN | 地理空间、TTL 自动过期、文本索引 |
| 复合索引规则 | 最左前缀原则 | ESR 规则(等值→排序→范围) |
| 索引对写入的影响 | 较大 | 较小(因为 Schema 灵活) |
记住:MySQL/PG 的复合索引按"最左前缀"使用;MongoDB 按 ESR 顺序。在你现在的学习阶段,能把一个关系型的 B-Tree 索引原理彻底搞懂,其他数据库的索引一通百通。
事务:数据库的"保险机制"
一句话讲清楚
事务 = 一组操作要么全部成功,要么全部撤销。转账就是一个天然的事务——A 扣钱、B 加钱,如果扣完钱系统崩了、B 没加钱,数据库必须能回退。
ACID 四特性
| 特性 | 含义 | 违反的例子 |
|---|---|---|
| Atomicity 原子性 | 全部成功或全部撤销 | 转账中途崩溃,钱扣了没到账 |
| Consistency 一致性 | 数据约束在事务前后始终成立 | 余额变成负数 |
| Isolation 隔离性 | 并发事务互不干扰 | 同时读到一个正在修改的中间状态 |
| Durability 持久性 | 提交后就永久保存 | 数据库重启后数据丢失 |
关系型的事务 vs 非关系型的事务
这是初学者最容易混淆的地方:
关系型数据库(MySQL/PG/SQLite):
- 事务是天生的、默认的。每一句 SQL 在没有显式
BEGIN时也是隐式事务。 - ACID 特性经过了数十年的打磨,非常成熟。你基本不用怀疑它的事务能力。
- MySQL InnoDB 的默认隔离级别是
REPEATABLE READ;PG 是READ COMMITTED(这也是为什么 PG 在高并发下性能更优的一个原因)。
MongoDB:
- 4.0 之前不支持多文档事务——这是 MongoDB 历史上被批评最多的一点。
- 4.0+ 支持了,但性能开销比关系型大。官方文档明确建议:能用嵌套文档解决的,不要用多文档事务。
- MongoDB 的设计哲学是"通过数据建模避免事务",而不是"用事务保证一致性"。这就是为什么它强调嵌套文档——一次更新一个文档天然就是原子的。
Redis:
- 没有传统意义的多键事务。它的事务(
MULTI/EXEC)只能保证一组命令原子执行,不会中途插入其他客户端的命令——但如果中间某条命令失败了,前面的不会回滚。 - 真正需要原子性的场景用 Lua 脚本——整个脚本在 Redis 中原子执行。
一句话总结:你的事务需求越强,越应该用关系型数据库。MongoDB 和 Redis 解决的是不同的问题。
锁:多个人同时操作怎么办?
一句话讲清楚
锁 = 并发控制的机制。两个请求同时想修改同一条数据,数据库用锁来排队和保护。
锁的层次:从粗到细
数据库级锁 → 表级锁 → 页级锁 → 行级锁
(很少用) (MyISAM) (少见) (InnoDB/PG/MongoDB)行锁是当前所有主流数据库的默认选择(MySQL InnoDB、PG、MongoDB WiredTiger)。它让并发操作互不干扰——A 改第 1 行、B 改第 2 行,互不影响。
但行锁有一个关键前提:WHERE 条件必须走索引。如果查询条件没命中索引,会发生全表扫描 → 锁全表。这也是为什么索引不仅仅关乎查询速度,还关乎并发能力。
乐观锁 vs 悲观锁
| 悲观锁 | 乐观锁 | |
|---|---|---|
| 思路 | "肯定有人跟我抢,先锁住" | "大概没人跟我抢,改了再说,冲突了重试" |
| 实现 | SELECT ... FOR UPDATE | 版本号/时间戳 |
| 适合 | 冲突多的场景(秒杀扣库存) | 冲突少的场景(编辑个人信息) |
| 代表 | MySQL FOR UPDATE | 几乎所有数据库都可以在应用层实现 |
中级后端工程师的核心能力之一,就是在"读多写少用乐观锁,写冲突多用悲观锁"之间做判断。你现在不需要精通,但心里要有这个概念。
新手如何高效学习数据库?
你现在遇到的问题很典型:工作中暂时用不上,学完容易忘。下面是我推荐的策略:
1. 先通后专,建立骨架
不要一上来就深入研究 MySQL 的 InnoDB 锁机制。先通读一个数据库的"基础篇 + 进阶篇"形成骨架,知道大概有哪些话题,然后再在自己感兴趣的领域深挖。
推荐顺序:MySQL 基础 → MySQL 进阶 → 挑一个你感兴趣的专题深挖(索引原理、事务隔离、慢查询优化三选一)。
2. 用可视化工具辅助记忆
你现在已经在用的 MySQL Workbench / DB Browser / Compass / pgAdmin / RedisInsight 不要只当"执行 SQL 的工具"。用它们的 Table Inspector、Schema 分析、Explain Plan 等功能可视化地看索引结构、查询计划和数据分布——人脑对图形化的记忆远强于纯文本。
3. 做小型项目,在项目中记住
单纯对着文档敲 SQL 很快就会忘。建一个小项目——哪怕是一个命令行工具、一个本地 TODO 应用——让 CRUD、索引、事务有"真实的场景"。比如:
- 做一个本地记账工具(SQLite),每次记账是一笔事务
- 做一个 URL 短链接服务(Redis 做缓存),实现访问计数
- 做一个博客搜索(ES),把文章导入后用全文搜索查
在"需要"中学,比在"应该"中学效率高 10 倍。
4. 面试驱动复习
当你需要一个"复习清单"时,打开这个文档系列的"进阶篇"末尾的 checklist——那是面试的常考点。把它们一条条过一遍,看能不能用自己的话讲清楚"这是干什么的、为什么要用、不用会怎样"。
5. 建立知识触达机制
大脑遗忘是正常的。你需要的是"快速唤醒"的能力。建立你自己的 TL;DR 速查表(比如记住"索引=目录、事务=保险、锁=排队"这三句话),忘了的时候 30 秒即可恢复上下文。