/knowledge/advanced-database-systems
高级数据库系统
当你运行一个查询时,数据库内部正在发生什么。它如何规划、如何防止成千上万的用户破坏彼此的工作,以及如何跨机器扩展而不致崩溃。
- 学于
- 高级数据库系统数据科学硕士
- 时间
- 墨尔本大学,2023–2024
- 应用于
- Power BI 数据层 · 扩展
- 阅读 / 复习
- 约 16 分钟阅读2026-06-25
数据库系统页讲了关系模型、SQL,以及为什么 数据库胜过文件。这是续篇:引擎内部究竟在发生什么。它如何把你的 SQL 变成一个 快速的计划?成千上万的并发用户如何不互相践踏?它如何在崩溃中幸存,或拆分到上百台机器 上?正是这些问题,把「我会写查询」与「我理解我把数据押在其上的这个系统」区分开来。
它建立在基础页之上,并在我们走向分布式时连到 集群与云计算。反复出现的主题:每一个 强大的特性都是一个权衡,而知道一个系统做了哪个权衡,就告诉你它在事情变难时如何 表现。
01
引擎盖之下
当你运行一个 SQL 查询时,你说的是你想要什么,而从不说如何去拿——那道声明式的 鸿沟正是要点所在。在幕后,数据库把那个请求变成一个可执行的计划,并尽可能高效地运行它。 让这件事成立的组件——优化器、事务管理器、存储引擎——正是我们在这里要拆开来看的。理解 它们,才让你能够诊断一个慢查询或一个神秘的死锁,而不是靠猜。
02
查询优化
同一个查询可以用许多种方式执行——先读哪张表、用哪个索引、用哪种连接算法——而它们的速度可能 相差几个数量级。查询优化器就是做选择的大脑,它是任何数据库中最 精密的软件之一。
它分阶段工作:解析 SQL,生成候选的执行计划,用关于数据的统计(有多少行、 值如何分布)估计每个计划的成本,再挑出最便宜的。这就是基于成本的 优化——优化器实际上是在预测哪个计划会触及最少的行、做最少的 I/O。这就是为什么让 表统计保持最新很要紧,也是为什么当优化器的估计与现实发生偏离时,同一个查询会突然变慢。
03
事务与并发
基础页引入了事务与 ACID 保证。难处在于:当成千上万的事务同时运行时,仍要把它们兑现—— 这就是并发控制,也是数据库赚到身价之处。让事务天真地并行,它们就会互相破坏 (一个读到另一个正改到一半的值);让它们严格地一次只跑一个,系统就慢到停摆。这份工作是: 在尽可能多地允许并行的同时,保住「每个事务都像是独自运行」的错觉。
数据库以一个隔离级别的旋钮来提供这点,而有两大类策略来强制实现它:
- 加锁——一个事务锁住它触及的数据,于是其他事务必须等待。安全,但容易产生 争用,并且会造成死锁(两个事务各自等着对方持有的锁),数据库会检测到并 通过中止其中一个来打破它。
- MVCC(多版本并发控制)——不加锁,而是为每一行保留多个版本,于是 读者看到一个一致的快照,而写者创建新的版本。读者从不阻塞写者,反之亦然。Postgres 与 大多数现代数据库正是靠它获得高并发,它也是读密集分析工作的更好默认。
04
存储内部
数据在磁盘上如何物理地排布,决定了性能,而有两种主导的设计:
- B 树——大多数关系索引背后的经典结构(来自基础页)。为快速读取而平衡, 适合读密集、就地更新的工作负载。几十年来的默认。
- LSM 树(日志结构合并树)——在内存中批量写入,再顺序刷写到磁盘,在后台 合并。它们让写极快,这就是为什么像 Cassandra 这样的写密集系统和许多 NoSQL 存储都用它——代价是读取速度有所牺牲。
两者之下都坐着那个让持久性成真的特性:预写日志(WAL)。在改动 实际数据之前,数据库先把该改动记录在一个只追加的日志里。如果它在操作中途崩溃,重启时会 重放日志,恢复到一个一致的状态——任何已提交的东西都不会丢失。它正是 ACID 中那个「D」 背后不光鲜的机制。
05
走向分布式
当数据或流量超出一台机器时,数据库必须铺散到许多台上——而集群与云页上同样的扩展现实在此 适用。两种技术:
- 分区 / 分片——把数据拆分到各节点(用户 A–M 在这儿、N–Z 在那儿),让每个 节点持有一片。这扩展了容量与写吞吐,但跨分片的查询变得更难。
- 复制——在多个节点上保留同一份数据的副本,以求容错(一个节点可以挂掉)与 读扩展(从任一副本提供读取)。但现在你必须让这些副本保持同步——而这正是它变深的地方。
06
一致性模型
你一复制数据,就撞上 CAP 定理(来自集群与云页):当节点之间的网络失效时, 你必须在一致性(每次读取都看到最新的写入)与可用性(每个请求 仍得到一个答复)之间选择。在分区期间你无法二者兼得。这迫使你选一个一致性模型:
- 强一致性——每次读取永远返回最新的写入。易于推理,但更慢、可用性更低, 因为节点在作答前必须协调。对银行余额而言是对的选择。
- 最终一致性——读取可能短暂地返回过时的数据,但所有副本经过一段时间会收敛。快且高可用——对一个社交信息流或点赞计数而言是对的选择,那里片刻的陈旧 无伤大雅。
两者都不「正确」;各自适合不同的需求。认出一个系统选了哪个模型,会确切地告诉你它在一个 节点或网络失效时如何表现——而那正是在生产中要紧的问题。
07
NoSQL 家族
为了得到那种规模与灵活性,NoSQL 数据库放松了刚性的关系模型。它们不是一种 东西,而是一个家族,各自为某一类数据而塑形:
- 文档型(MongoDB)——存储灵活的类 JSON 文档;当模式多变时很好用。
- 键值型(Redis)——一本巨大而快速的字典;非常适合缓存与会话。
- 宽列型(Cassandra)——铺散在许多节点上的巨表,为写而优化。
- 图型(Neo4j)——直接对实体与关系建模;为网络这样的关联数据而生。
权衡还是那个反复出现的:大多数 NoSQL 存储放弃一些关系保证(丰富的连接、严格的模式、 完整的 ACID),以换取在某一特定数据形状上的规模、灵活性或速度。它们是对关系数据库的 补充,而非取代——你挑选适合任务的那个存储。
08
分析型数据库
最后一个重要的分野——来自基础页的 OLTP vs OLAP 之分,被推到它在硬件上的 结论。事务型数据库逐行存储数据(读或写一整条记录很快)。分析型数据库——像 BigQuery、Snowflake、Redshift 这样的数据仓库——逐列存储它。列式存储对分析是变革性的:一个对某一列求和的查询,只从磁盘读那一列,而非每一行, 而打包在一起的相似值压缩得极好。这就是为什么同一个「在历史上的大查询」在仓库上几秒跑完、 在事务型数据库上要几分钟——也是为什么严肃的分析住在一个独立的、列式的存储里。
09
它在我工作中的体现
10