Skip to content

数据库从零到精通验收路线 ​

这一页用来回答一个很现实的问题:只看完这些文档,算不算真的会数据库?

答案不能靠“我看过 MySQL、Oracle、PostgreSQL、SQL Server 的介绍”来判断。真正学会数据库,要能把业务建模、SQL、索引、执行计划、事务、日志、恢复、复制、高可用、线上排查和面试表达串成闭环。

学数据库不是背名词,而是建立一套判断能力:面对一个业务需求,你能设计表;面对一条慢 SQL,你能解释慢在哪里;面对并发扣库存,你能保证不会错;面对宕机、误删、主从延迟、锁等待,你能知道数据库为什么会这样,以及该怎么恢复。

学完后必须达到什么水平 ​

能力合格标准不合格表现
SQL 基础能写增删改查、Join、聚合、分页、事务 SQL只会单表 select *
表设计能根据订单、支付、库存、采集任务设计字段、约束、索引字段全用字符串,没有唯一约束
索引能解释 B+Tree/B-tree 家族、联合索引、覆盖索引、回表、索引失效认为“加索引一定快”
执行计划能看懂访问方式、扫描行数、排序、临时表、回表、Join 顺序慢 SQL 只会猜
事务能解释 ACID、隔离级别、锁、MVCC、死锁、丢失更新只背四个英文单词
日志恢复能讲清 redo、undo、binlog、WAL、Redo、事务日志分别解决什么以为日志只是记录操作历史
高可用能理解主从复制、流复制、Data Guard、Always On、备份恢复边界以为主从就是复制一份数据
线上排查能按慢 SQL、锁等待、连接池、磁盘 IO、复制延迟拆问题只会重启数据库
数据库选型能说明 MySQL、PostgreSQL、Oracle、SQL Server 的差异和商业边界只说“哪个性能更好”
面试表达标准回答简洁,追问时能跳到原理和案例只背八股,没有解释链路

总学习流程 ​

零基础到能上项目,推荐按下面这条线走。不要一上来就背 MVCC,也不要只看面试题。

mermaid
flowchart TD
    A["第一阶段<br/>会写 SQL"] --> B["第二阶段<br/>会设计表"]
    B --> C["第三阶段<br/>理解索引"]
    C --> D["第四阶段<br/>看懂执行计划"]
    D --> E["第五阶段<br/>理解事务并发"]
    E --> F["第六阶段<br/>理解日志恢复"]
    F --> G["第七阶段<br/>理解复制高可用"]
    G --> H["第八阶段<br/>能做线上排查"]
    H --> I["第九阶段<br/>能做技术选型"]
    I --> J["第十阶段<br/>面试能讲清原理"]

这条线背后的原因是:

  1. 先学 SQL,是因为数据库最终接收到的是 SQL 或协议请求。
  2. 再学表设计,是因为错误模型会让后续所有查询和事务都变难。
  3. 再学索引,是因为索引是数据库性能的第一道门。
  4. 再学执行计划,是因为索引有没有用、慢在哪里都要靠计划验证。
  5. 再学事务,是因为商业系统最怕数据错。
  6. 再学日志,是因为提交、回滚、复制、恢复都离不开日志。
  7. 最后学排查和选型,是因为生产问题通常是多个机制叠加。

第一阶段:SQL 必须学到什么程度 ​

SQL 不是只会查一张表。商业系统常见需求包括:条件筛选、排序、分页、分组统计、Join、去重、存在性判断、批量更新、事务提交。

必会内容 ​

知识点要学到什么程度商业场景
select能选择字段、过滤、排序、分页用户列表、资产列表
insert能单条和批量插入采集结果入库
update能带状态条件更新支付成功更新订单
delete知道硬删和软删区别删除草稿、逻辑删除
join知道 inner/left join 结果差异订单关联用户、资产关联科室
group by能做聚合统计每日订单量、科室设备数
子查询知道何时可读、何时可能慢查询有异常记录的设备
事务 SQL知道 begin/commit/rollback扣库存、写流水

最小 Demo:订单和明细查询 ​

sql
create table order_main (
  id bigint primary key,
  order_no varchar(64) not null,
  user_id bigint not null,
  status int not null,
  total_amount decimal(12, 2) not null,
  created_at timestamp not null
);

create table order_item (
  id bigint primary key,
  order_id bigint not null,
  sku_id bigint not null,
  quantity int not null,
  price decimal(12, 2) not null
);

select o.order_no, o.total_amount, sum(i.quantity) as item_count
from order_main o
join order_item i on i.order_id = o.id
where o.user_id = 1001
  and o.status = 1
group by o.order_no, o.total_amount
order by o.created_at desc;

如果这一步都不熟,直接看 MVCC、redo log、WAL 会很虚,因为你还不知道数据库内部在为哪类请求服务。

第二阶段:表设计必须能独立完成 ​

表设计不是把 Java 对象字段原样搬进数据库。你要考虑数据类型、主键、唯一约束、非空、默认值、状态字段、时间字段、审计字段、索引和未来数据增长。

订单表设计示例 ​

sql
create table payment_order (
  id bigint primary key,
  order_no varchar(64) not null,
  user_id bigint not null,
  pay_channel varchar(32) not null,
  status int not null,
  amount decimal(12, 2) not null,
  paid_at timestamp null,
  created_at timestamp not null,
  updated_at timestamp not null,
  constraint uk_payment_order_no unique (order_no)
);

create index idx_payment_user_status_time
on payment_order(user_id, status, created_at);

为什么要这样设计 ​

设计原因不这样会怎样
order_no 唯一约束防止重复订单号应用并发 Bug 会写入重复订单
金额用 decimal避免浮点精度误差金额计算出现小数误差
状态字段单独存状态流转可控无法判断订单生命周期
时间字段完整方便查询、审计、归档排查问题没有时间线
联合索引匹配用户订单列表单列索引可能仍然扫描很多行

第三阶段:索引要理解到原理层 ​

索引的本质不是“让查询变快的开关”,而是数据库额外维护的一份有序数据结构。它能减少扫描范围,但会占空间,并拖慢写入。

mermaid
flowchart TD
    A["业务 SQL<br/>where user_id and status"] --> B{"有没有匹配索引"}
    B -- "有" --> C["沿索引树定位范围"]
    C --> D["扫描少量索引项"]
    D --> E{"查询字段是否都在索引里"}
    E -- "是" --> F["覆盖索引直接返回"]
    E -- "否" --> G["通过主键或行定位器回表"]
    B -- "没有" --> H["扫描大量数据页"]

必须讲清的几个问题 ​

问题标准理解
为什么 B+Tree/B-tree 适合数据库树高低、页内高扇出、key 有序、适合范围扫描
为什么 Hash 不适合作为通用索引等值快,但不支持范围、排序、前缀匹配
为什么联合索引有顺序B+Tree 按组合 key 排序,前导列决定整体有序性
覆盖索引为什么快查询字段都在索引里,可以减少回表
覆盖索引为什么仍可能慢扫描范围很大、深分页、排序、缓存未命中仍会慢
为什么有索引也可能不用优化器估算全表扫描更便宜、统计信息不准、条件选择性差

更多原理看:B+Tree、B-Tree 与数据库索引家族。

第四阶段:执行计划必须会看来源 ​

执行计划不是神秘输出,它来自优化器对统计信息、索引、条件选择性、Join 成本、排序成本的估算。

mermaid
flowchart TD
    A["SQL"] --> B["解析和语义检查"]
    B --> C["读取统计信息"]
    C --> D["枚举访问路径"]
    D --> E["估算扫描行数和成本"]
    E --> F["选择执行计划"]
    F --> G["执行器按计划访问数据"]

MySQL EXPLAIN rows 是怎么来的 ​

rows 不是实际扫描行数,而是优化器根据统计信息估算出来的行数。估算来源包括:

  1. 表的行数统计。
  2. 索引基数,也就是不同 key 值的大致数量。
  3. 条件选择性,比如 status = 1 可能命中多少比例。
  4. 范围条件边界,比如时间范围、主键范围。
  5. 统计信息是否过期。

如果统计信息不准,rows 就可能偏离真实情况,优化器可能选错索引。详细看:MySQL EXPLAIN。

PostgreSQL EXPLAIN ANALYZE 为什么更适合验证 ​

PostgreSQL 的 EXPLAIN 只看计划,EXPLAIN ANALYZE 会真正执行 SQL 并给出实际耗时和实际行数。它适合验证“优化器估算”和“真实执行”差多少。

sql
explain analyze
select id, asset_no
from asset
where owner_id = 1001
order by created_at desc
limit 20;

如果估算 10 行,实际 100000 行,就说明统计信息、数据分布或条件设计有问题。

第五阶段:事务并发必须能解释现象 ​

商业系统里,事务不是为了背 ACID,而是为了让多条 SQL 要么一起成功,要么一起失败,并且并发执行时不把数据改错。

mermaid
flowchart TD
    A["扣库存请求"] --> B["开启事务"]
    B --> C["按商品 ID 查询库存并加锁"]
    C --> D{"库存是否足够"}
    D -- "足够" --> E["扣减库存"]
    E --> F["写库存流水"]
    F --> G["提交事务"]
    D -- "不足" --> H["回滚并返回失败"]

必须能解释的问题 ​

问题要点
脏读读到别人未提交的数据
不可重复读同一事务内两次读同一行结果不同
幻读同一范围两次读出现新行
丢失更新两个事务基于同一个旧值更新,后提交覆盖先提交
死锁两个事务互相等待对方持有的锁
长事务持锁久、版本无法清理、日志堆积、复制延迟

不同数据库实现不同:

数据库版本机制重点
MySQL InnoDBundo log 版本链 + Read View
PostgreSQLheap tuple 多版本 + xmin/xmax + VACUUM
OracleUndo + SCN 一致性读
SQL Server默认锁读,也可用行版本隔离,例如 RCSI

第六阶段:日志恢复要讲清边界 ​

数据库不会每次提交都把所有数据页完整刷到磁盘。它通过预写日志保证:先把可恢复的信息写安全,再允许数据页后续慢慢刷盘。

mermaid
flowchart TD
    A["事务修改数据"] --> B["生成内存中的脏页"]
    B --> C["先写事务日志"]
    C --> D["事务提交返回成功"]
    D --> E["后台异步刷数据页"]
    E --> F["宕机后用日志恢复"]

各数据库日志怎么对应 ​

数据库关键日志作用
MySQL InnoDBredo log崩溃恢复,保证已提交修改能恢复
MySQLundo log回滚和 MVCC 旧版本
MySQLbinlog主从复制和时间点恢复
PostgreSQLWAL崩溃恢复、流复制、归档恢复
OracleRedo崩溃恢复、Data Guard
OracleUndo回滚、一致性读、Flashback 基础
SQL ServerTransaction Log回滚、恢复、日志备份、Always On

这里最容易混淆:日志不是一类东西。redo/WAL/Transaction Log 更偏恢复已提交修改;undo 更偏回滚和版本可见性;binlog 更偏复制和逻辑恢复。

第七阶段:四大关系库差异要能横向比较 ​

不要把 MySQL、PostgreSQL、Oracle、SQL Server 学成四套孤立八股。它们都在解决同一批问题,只是实现取舍不同。

维度MySQLPostgreSQLOracleSQL Server
常见业务互联网交易、后台系统复杂 SQL、GIS、JSONB金融政企核心.NET 企业应用、BI
进程/线程模型多线程服务多进程模型常见实例 + 后台进程服务进程 + 工作线程
索引叫法B+TreeB-treeB-tree/Bitmap聚集/非聚集 B-tree
版本机制undo 版本链tuple 多版本Undo + SCN锁或行版本
日志redo + binlogWALRedo + 归档Transaction Log
排查重点慢日志、锁、主从延迟VACUUM、膨胀、锁等待事件、AWR、Undo等待、TempDB、参数嗅探

更多详细对比看:主流关系库差异与选型。

第八阶段:商业项目训练 ​

要判断自己是不是会数据库,不能只看文档。下面这些练习做完,才算真正从“知道概念”走到“能落地”。

本阶段可以直接配合 关系型数据库商业场景训练营 做。训练营把订单支付、医疗资产采集、库存扣减、覆盖索引仍慢、四大数据库建表差异和 UPDATE 原理拆成了可执行步骤;本页负责验收标准,训练营负责动手过程。

练习一:订单支付系统 ​

你要完成:

  1. 设计订单表、支付表、订单明细表、支付流水表。
  2. 给订单号、支付流水号加唯一约束。
  3. 写“创建订单”“支付成功回调”“查询用户订单列表”SQL。
  4. 给高频查询设计联合索引。
  5. 用执行计划验证索引是否生效。
  6. 模拟并发支付回调,保证同一订单只处理一次。
  7. 解释为什么需要幂等状态条件,例如 where status = 'UNPAID'。

关键点:支付回调不能只靠应用判断,数据库唯一约束和状态更新条件都要兜底。

练习二:医疗数据采集平台 ​

你要完成:

  1. 设计设备表、采集任务表、采集结果表、异常事件表。
  2. 设备唯一标识要有唯一约束。
  3. 采集结果可能是大表,要考虑按时间归档或分区。
  4. 采集任务要能防重复执行。
  5. 写“查询某医院某科室最新异常设备”的 SQL。
  6. 写慢 SQL 后用执行计划定位扫描范围。
  7. 说明什么时候同步到 Elasticsearch,什么时候仍以关系库为准。

关键点:采集系统写入多、查询维度多,不能只靠单表无限堆数据。

练习三:库存扣减 ​

你要完成:

sql
update inventory
set stock = stock - 1
where sku_id = 10001
  and stock > 0;

然后解释:

  1. 为什么 stock > 0 要放在 SQL 条件里。
  2. 为什么要检查影响行数。
  3. 并发情况下数据库如何加锁。
  4. 如果事务里还要写流水,事务边界怎么放。
  5. 如果扣减失败,业务如何返回。

关键点:库存不是先查出来再在 Java 里减完写回那么简单,否则容易丢失更新或超卖。

第九阶段:线上问题排查模板 ​

遇到数据库问题,不要一上来改索引。先分类。

mermaid
flowchart TD
    A["数据库问题"] --> B{"表现是什么"}
    B -- "单条 SQL 慢" --> C["看执行计划"]
    C --> D["查扫描行数、索引、排序、回表"]
    B -- "接口整体慢" --> E["看连接池和数据库等待"]
    E --> F["查锁等待、CPU、IO、慢日志"]
    B -- "写入慢" --> G["查事务大小和日志刷盘"]
    G --> H["查磁盘延迟、复制延迟、大事务"]
    B -- "偶发卡死" --> I["查锁、死锁、长事务"]
    I --> J["定位阻塞会话和事务边界"]

常用判断 ​

现象第一怀疑第二怀疑
SQL 扫描行数很大索引不匹配条件选择性差
有索引仍慢回表多、排序、扫描范围大缓存未命中、锁等待
突然整体慢锁等待、大事务磁盘 IO、连接池耗尽
从库延迟大事务、从库执行慢并行复制不足
PostgreSQL 表越来越慢VACUUM 跟不上长事务阻止清理
SQL Server 偶发慢参数嗅探TempDB、Key Lookup
Oracle 报快照过旧Undo 被覆盖长查询或 Undo 不足

第十阶段:面试和知识点如何分开 ​

面试页只应该放标准回答和追问,不应该把所有原理塞在里面。正确方式是:

mermaid
flowchart TD
    A["面试问题"] --> B["30秒标准回答"]
    B --> C["追问点"]
    C --> D["跳转知识点页"]
    D --> E["原理流程图"]
    E --> F["Demo和排查案例"]

例如面试问:“MySQL 的 redo log 和 binlog 有什么区别?”

标准回答应该简洁:

text
redo log 是 InnoDB 的物理重做日志,用于崩溃恢复;binlog 是 MySQL Server 层的逻辑日志,主要用于主从复制和时间点恢复。事务提交时需要两阶段提交协调 redo 和 binlog,避免主库恢复结果和从库复制结果不一致。

如果追问原理,再跳到:redo log 与 binlog 和 InnoDB 更新事务全过程原理。

学习验收题 ​

如果下面这些问题你能讲清楚,并能写出对应 SQL 或排查命令,数据库主线才算过关。

  1. 一条 select 从客户端到 InnoDB/存储层经历哪些步骤?
  2. MySQL 有几层?三层和五层分别怎么讲?
  3. EXPLAIN rows 是实际行数吗?来源是什么?
  4. 覆盖索引为什么仍然可能慢?
  5. B+Tree 为什么适合 MySQL?Oracle、PostgreSQL、SQL Server 说 B-tree 是什么意思?
  6. MVCC 为什么能让普通读不阻塞写?
  7. MySQL undo log 和 MVCC 是什么关系?
  8. redo log 和 binlog 为什么需要两阶段提交?
  9. PostgreSQL 为什么需要 VACUUM?长事务会造成什么问题?
  10. Oracle SCN 在一致性读里有什么作用?
  11. SQL Server 聚集索引和非聚集索引有什么区别?
  12. 线上数据库突然慢,你按什么顺序排查?
  13. ES 和 MySQL 数据一致性失败怎么补偿?
  14. Redis 和数据库一致性为什么不能靠“同时写成功”解决?
  15. 分库分表后跨库事务、排序、分页、唯一 ID 怎么处理?

关联学习入口 ​

方向入口
数据库总原理关系型数据库底层原理
四大关系库对比主流关系库差异与选型
MySQL 主线MySQL 从零到生产级掌握
PostgreSQL 主线PostgreSQL 从零到生产级掌握
Oracle 主线Oracle 从零到生产级掌握
SQL Server 主线SQL Server 从零到生产级掌握
Redis 主线Redis 从零到生产级掌握
Elasticsearch 主线Elasticsearch 从零到生产级掌握

数据库从零到精通不是看完一页文档,而是能把“需求怎么建模、SQL 怎么执行、索引为什么生效、事务为什么正确、日志为什么可恢复、线上为什么会慢、不同数据库为什么不同”连成一条线。后续每个具体数据库页都应该围绕这条线展开。