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 与 binlogInnoDB 更新事务全过程原理

学习验收题

如果下面这些问题你能讲清楚,并能写出对应 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 怎么执行、索引为什么生效、事务为什么正确、日志为什么可恢复、线上为什么会慢、不同数据库为什么不同”连成一条线。后续每个具体数据库页都应该围绕这条线展开。