数据库基础体系 · 第 1/139 篇。文章以各产品官方稳定版本的公开语义为准;示例会明确引擎、事务与部署边界。

数据库完整学习路线:从关系模型、事务索引到分布式与向量检索

数据库学习不能从“记住几条 SQL”开始,也不能止于“会调用 ORM”。一条完整路线应当回答以下问题:

  • 数据如何被建模,为什么表结构能够表达业务约束?
  • SQL 如何从声明式查询变成执行计划?
  • 多个并发事务同时读写时,数据库如何保持正确性?
  • 索引为什么能加速查询,又为什么会拖慢写入?
  • 单机容量、可用性或吞吐不足时,复制、分片和共识分别解决什么问题?
  • 向量检索与传统等值、范围、排序查询有什么根本差异?
  • 在真实系统中,如何在正确性、性能、可运维性之间做取舍?

本文以关系型数据库为主线,补充分布式数据库和向量检索。SQL 示例以 PostgreSQL 语义为主,并在需要时说明 MySQL 8.4 的差异。SQL 标准、具体数据库实现和工程经验不是同一层次,文中会明确区分。


一、先建立总模型:数据库到底在管理什么

数据库可以抽象为一个维护状态的系统:

St+1=T(St,Ot)S_{t+1} = T(S_t, O_t)

其中:

  • StS_t 是时刻 tt 的数据库状态;
  • OtO_t 是一次操作,例如插入、更新、删除或查询;
  • TT 是数据库执行操作并维护约束的过程;
  • St+1S_{t+1} 是操作完成后的新状态。

对于并发系统,问题不再是单个操作是否正确,而是多个操作交错执行后,最终状态是否等价于某个正确的串行执行顺序。

因此,数据库学习可以分成四个层次:

  1. 数据模型:数据是什么、实体如何关联、哪些状态合法。
  2. 查询与存储:如何表达访问需求,如何用索引和执行计划降低代价。
  3. 事务与并发:多个操作如何组成可靠的状态变更。
  4. 分布式与新型检索:数据跨节点、跨分片或从精确匹配扩展到相似性匹配。

关系模型、事务、索引、分布式数据库和向量检索并不是互相独立的知识点。它们共同回答的是:如何在数据规模、并发和故障存在时,仍然得到可解释、可验证的结果。


二、关系模型:先定义数据,再定义操作

2.1 关系、元组、属性和域

关系模型中的“关系”可以近似理解为一张表,但它比表格概念更严格。

  • 关系(relation):一个满足模式约束的元组集合。
  • 元组(tuple):关系中的一行。
  • 属性(attribute):元组中的一个命名字段。
  • 域(domain):某个属性允许取值的集合,例如整数、日期或有限状态值。
  • 关系模式(relation schema):关系名以及属性定义,例如

    Student(id,name,department)Student(id, name, department)

在数学关系模型中,关系是集合,因此理论上没有重复元组,也没有固定顺序。SQL 表则是工程实现:

  • SQL 查询结果默认不保证顺序,除非使用 ORDER BY
  • SQL 表在没有唯一约束时可能存在重复行;
  • SQL 引入了 NULL,这会使逻辑从二值逻辑变成带未知值的三值逻辑。

例如:

CREATE TABLE student (
    student_id bigint PRIMARY KEY,
    name       text NOT NULL,
    department text
);

这里:

  • student_id 是属性;
  • bigint 定义其域的一部分;
  • PRIMARY KEY 要求值唯一且非空;
  • department 没有 NOT NULL,因此可以是 NULL

NULL 不是字符串 "NULL",也不是数字 0。它表示“未知”或“不适用”。例如:

SELECT *
FROM student
WHERE department = NULL;

通常得不到预期结果,因为 department = NULL 的结果是 UNKNOWN,而 WHERE 只保留结果为 TRUE 的行。正确写法是:

SELECT *
FROM student
WHERE department IS NULL;

2.2 键:如何唯一识别一行

超键、候选键和主键

设关系为 RR,属性集合为 KK

  • KK 能唯一确定一行,则 KK超键
  • KK 是超键,且删除其中任意属性后都不再唯一,则 KK候选键
  • 从候选键中选出的一个作为主要标识,就是主键

例如:

Enrollment(student_id, course_id, semester, score)

如果同一学生在同一学期同一门课程只能有一条成绩,则:

(student_id,course_id,semester)(student\_id, course\_id, semester)

是候选键。单独的 student_id 不是键,因为一个学生可以选多门课。

SQL 表达为:

CREATE TABLE enrollment (
    student_id bigint NOT NULL,
    course_id  bigint NOT NULL,
    semester   text   NOT NULL,
    score      numeric(5, 2),
    PRIMARY KEY (student_id, course_id, semester)
);

自然键和代理键

  • 自然键:业务中本来就有意义的标识,例如身份证号、ISBN。
  • 代理键:系统生成的标识,例如自增整数、UUID。

代理键不等于业务唯一性。下面的设计仍然需要业务唯一约束:

CREATE TABLE account (
    account_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    email      text NOT NULL UNIQUE
);

account_id 负责稳定引用;email 负责防止两个账户使用相同邮箱。只设置代理主键而不设置业务唯一约束,会把数据合法性留给应用层,且容易在并发下失效。


2.3 外键和引用完整性

外键表达的是跨表约束:

CREATE TABLE orders (
    order_id  bigint PRIMARY KEY,
    user_id   bigint NOT NULL,
    CONSTRAINT fk_orders_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
);

它要求每个 orders.user_id 都能在 app_user.user_id 中找到对应值,除非该外键允许 NULL

删除或更新被引用行时,需要定义动作:

FOREIGN KEY (user_id)
REFERENCES app_user(user_id)
ON DELETE RESTRICT

常见动作包括:

  • RESTRICTNO ACTION:存在引用时拒绝删除;
  • CASCADE:级联删除或更新;
  • SET NULL:将引用列设为 NULL,前提是列允许为空;
  • SET DEFAULT:设置为默认值。

CASCADE 不是“更方便的默认选择”。如果父表是一条核心业务记录,级联删除可能在一次操作中删除大量数据。删除策略必须符合业务生命周期。


三、函数依赖与规范化:为什么表结构会产生异常

3.1 函数依赖

函数依赖表示:

XYX \rightarrow Y

含义是:对关系中的任意两行,只要它们在属性集合 XX 上相等,那么它们在属性集合 YY 上也必须相等。

例如:

Student(student_id, student_name, department_id, department_name)

通常有:

student_idstudent_name,department_idstudent\_id \rightarrow student\_name, department\_id

以及:

department_iddepartment_namedepartment\_id \rightarrow department\_name

因此:

student_iddepartment_namestudent\_id \rightarrow department\_name

但这并不表示 department_name 应该直接存储在学生表中。


3.2 反例:重复数据和更新异常

假设课程选课表如下:

student_id student_name course_id course_name teacher
1 张三 C1 数据库 李老师
2 李四 C1 数据库 李老师

此时课程名称和教师信息被重复存储。

更新异常

如果课程 C1 更换教师,只更新了第一行,就得到:

course_id course_name teacher
C1 数据库 王老师
C1 数据库 李老师

数据库中出现两个互相矛盾的事实。

插入异常

如果课程尚未有学生选修,就无法只依靠这张表记录课程本身。

删除异常

如果删除课程 C1 的最后一名学生,课程和教师信息也会被一起删除。

这些异常并不是“数据量大后才出现”,而是由依赖关系和存储粒度不匹配造成的。


3.3 第一、第二、第三范式

第一范式:属性值应当原子化

第一范式通常要求每个字段存放一个不可再分的值,而不是列表或重复组。

不推荐:

user_id | phone_numbers
1       | 138...,139...

推荐拆成:

user(user_id, ...)
user_phone(user_id, phone_number)

不过“原子”依赖业务语义。一个完整地址是否应拆成省、市、街道,不是数据库理论单独决定的,而取决于系统是否需要分别查询、排序和约束这些部分。

第二范式:消除对复合键的部分依赖

第二范式要求:

  1. 表已经满足第一范式;
  2. 每个非主属性都完全依赖于候选键,而不是只依赖复合键的一部分。

继续看:

Enrollment(student_id, course_id, student_name, course_name, score)

候选键为:

(student_id,course_id)(student\_id, course\_id)

依赖关系包括:

student_idstudent_namestudent\_id \rightarrow student\_name

course_idcourse_namecourse\_id \rightarrow course\_name

(student_id,course_id)score(student\_id, course\_id) \rightarrow score

student_name 只依赖 student_idcourse_name 只依赖 course_id,因此存在部分依赖。应拆为:

Student(student_id, student_name)
Course(course_id, course_name)
Enrollment(student_id, course_id, score)

第三范式:消除非键属性之间的传递依赖

第三范式要求表已经满足第二范式,并且非键属性不应依赖于另一个非键属性。

例如:

Employee(employee_id, department_id, department_name)

有:

employee_iddepartment_idemployee\_id \rightarrow department\_id

department_iddepartment_namedepartment\_id \rightarrow department\_name

所以:

employee_iddepartment_nameemployee\_id \rightarrow department\_name

department_name 通过 department_id 间接依赖员工主键,属于传递依赖。应拆为:

Employee(employee_id, department_id)
Department(department_id, department_name)

3.4 规范化不是越高越好

规范化的目标是让每个事实尽量只存储一次,并使约束清晰。它并不意味着任何重复列都必须删除。

例如订单明细中可以保存下单时的商品单价:

Product(product_id, current_price)
OrderItem(order_id, product_id, quantity, unit_price)

Product.current_price 表示当前价格;OrderItem.unit_price 表示历史成交价格。两者语义不同,即使数值相同,也不能简单合并。

这属于有意的反规范化,成立的前提是:

  1. 两个字段代表不同业务事实;
  2. 写入时明确快照时机;
  3. 可以解释它们何时应该不同;
  4. 有约束、测试或对账机制防止无意漂移。

反规范化常见于:

  • 读取路径极其频繁;
  • 连接多个大表成本过高;
  • 聚合结果需要缓存;
  • 历史快照必须保留;
  • 分布式环境中跨分片连接代价过高。

它的代价是更新路径更复杂,数据一致性责任从数据库约束部分转移到了事务、消息或重建任务。


四、SQL:从声明式查询到执行计划

4.1 SQL 的逻辑处理顺序

下面的 SQL:

SELECT u.user_id, COUNT(o.order_id) AS order_count
FROM app_user AS u
LEFT JOIN orders AS o
  ON o.user_id = u.user_id
WHERE u.status = 'active'
GROUP BY u.user_id
HAVING COUNT(o.order_id) >= 3
ORDER BY order_count DESC;

逻辑上大致按以下顺序理解:

  1. FROMJOIN 形成行集;
  2. WHERE 过滤普通行;
  3. GROUP BY 形成分组;
  4. 聚合函数计算每组结果;
  5. HAVING 过滤分组;
  6. SELECT 投影列;
  7. ORDER BY 排序;
  8. LIMIT 截断结果。

实际执行计划不必按照这个顺序执行。优化器可能先过滤、使用索引、改变连接顺序或采用哈希聚合,但结果必须符合 SQL 语义。

LEFT JOIN 的关键边界是:右表条件放在 ONWHERE 中,语义不同。

-- 保留没有已支付订单的用户
SELECT u.user_id, o.order_id
FROM app_user u
LEFT JOIN orders o
  ON o.user_id = u.user_id
 AND o.status = 'paid';

如果写成:

SELECT u.user_id, o.order_id
FROM app_user u
LEFT JOIN orders o
  ON o.user_id = u.user_id
WHERE o.status = 'paid';

那么没有订单的用户会因 WHERE o.status = 'paid'UNKNOWN 而被过滤,效果接近内连接。


4.2 SQL 不保证顺序

没有 ORDER BY 时,结果顺序没有语义保证。即使今天执行计划总是返回某种顺序,也不能把实现偶然性当作契约。

分页应使用稳定排序:

SELECT *
FROM orders
WHERE (created_at, order_id) < (:last_created_at, :last_order_id)
ORDER BY created_at DESC, order_id DESC
LIMIT 50;

这里使用 (created_at, order_id) 作为排序游标,避免只按时间排序时同一时间戳造成重复或漏项。


4.3 用执行计划验证假设

PostgreSQL:

EXPLAIN (ANALYZE, BUFFERS)
SELECT *
FROM orders
WHERE user_id = 42
  AND status = 'paid';

EXPLAIN 展示计划估算;ANALYZE 会实际执行查询并展示真实行数、耗时和循环次数。由于 EXPLAIN ANALYZE 会执行语句,对 UPDATEDELETE 等语句尤其要谨慎。

重点比较:

  • 估算行数与实际行数是否严重偏差;
  • 是否发生全表扫描;
  • 连接算法是否适合数据规模;
  • 是否发生大量磁盘排序;
  • BUFFERS 显示的数据页访问是否异常。

MySQL 可使用:

EXPLAIN ANALYZE
SELECT ...

具体输出格式和可用选项以对应版本为准。执行计划不是永久承诺:数据分布、统计信息、参数和版本变化都可能改变计划。


五、索引:用额外结构换取更低的访问代价

5.1 B-tree 索引解决什么问题

最常见的 B-tree 索引适合:

  • 等值匹配;
  • 范围查询;
  • 按索引顺序读取;
  • 部分前缀匹配。

例如:

CREATE INDEX orders_user_status_created_idx
ON orders (user_id, status, created_at DESC);

它可以帮助:

SELECT *
FROM orders
WHERE user_id = 42
  AND status = 'paid'
ORDER BY created_at DESC
LIMIT 20;

复合索引中,列顺序很重要。对于 (a, b, c),通常最容易支持:

  • a = ?
  • a = ? AND b = ?
  • a = ? AND b = ? AND c > ?

但只查询 b = ? 时,不能期待它像以 b 开头的索引一样高效。所谓“最左前缀”是常见 B-tree 实现的访问规律,不是所有索引类型的统一定律。


5.2 选择性不是唯一标准

选择性可以粗略理解为过滤后剩余行的比例。低选择性的列,例如只有 true/false 两种值,单独建立索引通常收益有限,但以下情况仍可能有价值:

  • true 只占极少数;
  • 查询同时包含其他列;
  • 可以使用部分索引;
  • 索引可以直接提供排序;
  • 索引覆盖了查询所需列。

PostgreSQL 示例:

CREATE INDEX orders_unpaid_idx
ON orders (created_at)
WHERE status = 'unpaid';

这类部分索引只索引满足谓词的行。查询条件必须能够被优化器证明与索引谓词兼容,否则不一定使用该索引。


5.3 索引的代价

索引不是免费的缓存。每次插入、删除或更新相关列时,数据库都要维护索引结构,代价包括:

  • 写放大;
  • 额外磁盘空间;
  • 缓存占用;
  • 页分裂或维护成本;
  • 批量写入速度下降;
  • 统计和维护复杂度增加。

因此索引设计必须从真实查询出发,而不是“每个字段都建索引”。

还要注意表达式是否破坏索引使用:

-- 可能无法直接使用普通 created_at 索引
WHERE DATE(created_at) = DATE '2025-01-01'

可以改写为范围:

WHERE created_at >= TIMESTAMP '2025-01-01 00:00:00'
  AND created_at <  TIMESTAMP '2025-01-02 00:00:00'

但时区语义必须先明确。若业务按用户时区计算日期,不能简单套用 UTC 边界。


5.4 不同索引类型的边界

以 PostgreSQL 为例:

  • B-tree:等值、范围和排序的通用选择;
  • Hash:等值比较,适用范围较窄;
  • GIN:倒排结构,常用于数组、全文检索等;
  • GiST:可扩展的通用搜索树,常用于范围、几何等;
  • BRIN:利用物理存储顺序压缩索引,适合超大且与物理顺序相关的表。

MySQL InnoDB 的主要索引结构是 B-tree 族实现;索引、聚簇主键和二级索引的存储组织与 PostgreSQL 不同,不能直接把一种引擎的页结构经验套到另一种引擎。


六、事务:把多步操作变成一个正确状态变化

6.1 ACID 的准确含义

事务是数据库执行的一组操作单元。ACID 包括:

  • 原子性(Atomicity):事务中的操作要么整体生效,要么整体不生效;
  • 一致性(Consistency):事务提交前后都满足数据库约束和业务不变量;
  • 隔离性(Isolation):并发事务之间的可见性和交错方式受到规定;
  • 持久性(Durability):提交成功后,即使随后发生崩溃,数据也应按数据库承诺保留。

一致性不是数据库凭空知道的业务正确性。数据库能直接维护主键、外键、唯一性、检查约束等;“账户余额不能为负”“库存不能超卖”则需要由约束、锁、事务逻辑共同表达。


6.2 一个完整转账事务

BEGIN;

UPDATE account
SET balance = balance - 100
WHERE account_id = 1
  AND balance >= 100;

-- 应用检查受影响行数必须为 1。
-- 若为 0,表示账户不存在或余额不足,应回滚。

UPDATE account
SET balance = balance + 100
WHERE account_id = 2;

-- 应用还应检查第二次更新是否成功。
COMMIT;

事务的正确性不只在于写了 BEGINCOMMIT,还在于:

  1. 每个必要条件都被检查;
  2. 任一步失败都会回滚;
  3. 锁定或并发控制覆盖了真正的不变量;
  4. 客户端断线、超时和重试不会造成重复业务效果。

如果第二个账户不存在,而应用没有检查影响行数,第一笔扣款可能仍被提交,转账就不再保持守恒。

更稳妥的建模方式是对两个账户都先锁定,并固定锁定顺序,降低死锁概率:

BEGIN;

SELECT account_id, balance
FROM account
WHERE account_id IN (1, 2)
ORDER BY account_id
FOR UPDATE;

-- 应用在事务内检查两行均存在、余额充足

UPDATE account
SET balance = CASE account_id
    WHEN 1 THEN balance - 100
    WHEN 2 THEN balance + 100
END
WHERE account_id IN (1, 2);

COMMIT;

FOR UPDATE 的具体锁行为依赖数据库和隔离级别;它不是跨系统的万能“禁止一切并发”。


七、隔离级别与异常现象

7.1 三类经典异常

考虑两个并发事务:

脏读

事务 B 读取事务 A 尚未提交的数据。如果 A 回滚,B 读到的内容从未成为持久状态。

不可重复读

事务 B 两次读取同一行,中间事务 A 修改并提交,B 两次得到不同结果。

幻读

事务 B 按条件读取一组行,中间事务 A 插入或删除满足条件的行,B 再次按同一条件读取时,结果集合发生变化。

还存在更一般的写偏差(write skew)

  1. 两个事务分别读取同一业务规则;
  2. 每个事务修改不同的行;
  3. 单独看每个事务都合法;
  4. 一起提交后,联合状态违反规则。

例如规则是“至少一名值班医生保持在岗”。两个事务分别看到两名医生都在岗,然后各自把不同医生设为休息,最终没有人值班。即使没有两个事务更新同一行,也可能破坏约束。


7.2 隔离级别不是一个跨产品完全相同的实现

SQL 标准定义了隔离级别及其允许现象,但具体数据库的实现和额外保证可能不同。

Read Uncommitted

允许最低级别的隔离。在一些实现中可能允许脏读;在 PostgreSQL 中,READ UNCOMMITTED 实际按更高的 READ COMMITTED 处理,而不是实现真正的脏读。

Read Committed

每条语句看到的通常是语句开始时已提交的数据。PostgreSQL 中,同一事务内两条普通 SELECT 可能看到不同快照。MySQL InnoDB 的具体可见性也受其 MVCC 实现影响。

Repeatable Read

目标是让事务内重复读取更稳定,但“可重复读”不等于所有并发业务规则都自动安全。PostgreSQL 的实现基于事务快照,并对某些冲突报告序列化失败;MySQL InnoDB 的默认隔离级别通常是 Repeatable Read,并结合间隙锁等机制处理部分范围并发。两者不能只根据名称推断完全相同的行为。

Serializable

要求并发执行结果等价于某种串行顺序。数据库可能通过锁、谓词锁、SSI 或其他机制实现。代价是:

  • 冲突时阻塞更多;
  • 可能出现死锁;
  • 可能出现序列化失败;
  • 应用必须能够重试整个事务。

序列化失败不是数据库“坏了”,而是数据库拒绝了无法安全排列的并发结果。


7.3 用例子理解写偏差

假设表中有值班记录:

CREATE TABLE doctor_duty (
    doctor_id bigint PRIMARY KEY,
    on_call   boolean NOT NULL
);

错误逻辑:

事务 A:查询 on_call=true,发现医生 1 和 2 都在岗
事务 B:查询 on_call=true,发现医生 1 和 2 都在岗
事务 A:将医生 1 设为不在岗
事务 B:将医生 2 设为不在岗
A、B 都提交

如果约束是“至少一人值班”,单纯锁住各自要更新的行不一定足够,因为两个事务的决定依赖的是集合条件,而不是某一行。

解决路径有三类:

  1. 使用 SERIALIZABLE,让数据库检测并发冲突;
  2. 引入一个所有变更都必须锁定的汇总行;
  3. 将规则改写为数据库可直接约束的结构,例如使用唯一约束或状态表。

选择哪一种取决于写入频率、冲突概率和业务模型。不能只说“加锁”而不说明锁住的对象与不变量之间的关系。


7.4 死锁与重试

死锁是两个或多个事务互相等待:

事务 A 持有行 1,等待行 2
事务 B 持有行 2,等待行 1

数据库通常会检测死锁并回滚其中一个事务。应用应:

  • 对死锁和序列化失败进行有限次数重试;
  • 每次重试重新开启完整事务;
  • 使用稳定的锁定顺序;
  • 缩短事务时间;
  • 不在事务中执行不可控的远程调用。

不能只重试最后一条 SQL。事务中间可能已经读取了不同数据,必须从事务起点重新执行。


八、MVCC、锁与持久化:事务背后的机制

8.1 MVCC 的基本思想

MVCC(多版本并发控制)不是让数据库只有一份正在变化的数据,而是让读者根据快照判断哪些版本可见。

抽象地说,一个事务读取数据时会有快照 VV。对某条记录的版本 rr

visible(r,V)={true,若版本创建事务已提交且符合快照false,否则visible(r, V) = \begin{cases} true, & \text{若版本创建事务已提交且符合快照}\\ false, & \text{否则} \end{cases}

不同数据库的版本元数据、清理方式和锁实现不同,但核心目标类似:读写在一定范围内可以并行,读者不必总等待写者。

MVCC 不会消除所有锁:

  • 更新同一行仍可能相互等待;
  • 唯一约束检查需要协调;
  • 外键检查需要协调;
  • 范围约束和串行化可能需要更强的冲突检测。

8.2 WAL 与崩溃恢复

WAL(Write-Ahead Logging,预写日志)的基本原则是:数据页落盘前,描述该修改的日志应先持久化。

崩溃恢复大致经历:

  1. 从检查点或日志起点开始扫描 WAL;
  2. 重做已提交或需要重做的修改;
  3. 撤销或忽略未完成事务的影响;
  4. 恢复到符合日志语义的状态。

因此“提交成功”不仅意味着 SQL 执行结束,还涉及日志是否满足持久化配置。同步提交、异步复制和客户端确认策略会影响“客户端看到成功”与“副本已经持久化”之间的边界。


九、分布式数据库:把单机问题扩展到网络故障

“分布式数据库”不是一个单一产品类别。它可能指:

  • 主从或多副本数据库;
  • 分片数据库;
  • 分布式 SQL 数据库;
  • 在应用层组合的多个数据库;
  • 带分布式事务和共识协议的系统。

这些系统必须面对单机没有的故障:网络延迟、网络分区、节点宕机、消息重复、时钟不一致、部分成功和副本落后。


9.1 复制:同一数据的多个副本

典型主副本写入流程:

客户端
  ↓
主节点接收写请求
  ↓
写入本地日志
  ↓
发送日志给副本
  ↓
根据确认策略向客户端返回成功

如果采用异步复制:

主节点提交成功 → 客户端收到成功
副本复制稍后完成

主节点此时宕机,最近提交的数据可能尚未到达副本,形成复制丢失窗口。

如果采用同步或法定人数确认:

ackquorumack \geq quorum

则提交延迟更高,但副本故障时通常能提供更强的数据保留保证。这里的 quorum 不是固定含义:需要结合副本数、故障域、日志持久化和选主规则理解。

读副本的真实边界

读副本可能存在:

  • 复制延迟;
  • 读到旧数据;
  • 切换期间短暂不可用;
  • 路由到错误拓扑;
  • 读取确认已写入但副本尚未应用的数据。

因此“写后立刻读”若要求读到刚写入的数据,需要:

  • 读主节点;
  • 使用会话粘性;
  • 使用带位置或时间约束的读;
  • 或等待副本追平。

只写“主库负责写、从库负责读”并不能自动保证读写一致性。


9.2 CAP 的正确使用方式

CAP 讨论的是网络分区发生时,在一致性(Consistency)和可用性(Availability)之间的选择:

  • 一致性:客户端得到的结果符合系统定义的单一、最新状态;
  • 可用性:每个非故障节点收到请求后都能返回结果;
  • 分区容错性:网络分区发生时系统仍尝试运行。

在真实分布式系统中,网络分区不能被可靠地排除,因此讨论通常聚焦于分区时更偏向一致性还是可用性。

CAP 不是“数据库只能选两个”的日常性能定理,也不能直接推出某产品“绝对 CP”或“绝对 AP”。还必须区分:

  • 强一致读;
  • 线性一致写;
  • 最终一致复制;
  • 会话一致性;
  • 可串行化事务。

9.3 分片:把数据拆到多个节点

分片函数可以写成:

shard=h(key)modNshard = h(key) \bmod N

其中:

  • keykey 是分片键;
  • hh 是哈希函数;
  • NN 是分片数量。

哈希分片通常分布均匀,但范围查询不友好。范围分片例如按租户、时间或 ID 区间组织,范围查询较自然,却可能产生热点。

分片键选择决定很多能力:

  • 是否能在单分片完成事务;
  • 是否能避免跨分片连接;
  • 数据是否均匀;
  • 扩容是否需要大量迁移;
  • 租户隔离是否清晰。

跨分片查询的数据流通常是:

协调节点解析查询
  ↓
计算目标分片
  ↓
并发发送子查询
  ↓
各分片局部过滤、排序、聚合
  ↓
协调节点合并结果

例如全局 ORDER BY created_at LIMIT 20,每个分片至少要返回足够候选,协调节点再进行归并。若每个分片只返回 20 行,在某些过滤和排序条件下可能无法得到正确的全局前 20 行。


9.4 分布式事务与 2PC

两阶段提交(2PC)包含:

  1. Prepare 阶段:协调者询问参与者能否提交,参与者持久化必要状态并回复;
  2. Commit 阶段:协调者根据结果发送提交或回滚决定。

问题在于协调者或网络故障可能使参与者长期处于 prepared 状态,锁和资源不能立即释放。2PC 提供跨参与者原子提交能力,但会带来阻塞、协调和恢复复杂度。

如果业务允许,常见替代方式是:

  • 将强一致边界收敛到单分片;
  • 使用本地事务加可靠消息;
  • 使用 Outbox 表记录待发布事件;
  • 使用可补偿的 Saga 流程。

这些方案不是自动等价于 2PC。它们通常牺牲即时原子性,换取更高可用性和更容易扩展的执行模型。


十、向量检索:从精确匹配到相似性搜索

10.1 Embedding 是什么

Embedding 是把文本、图片、用户行为或其他对象编码成固定维度的数值向量:

x=(x1,x2,,xd)x = (x_1, x_2, \ldots, x_d)

其中 dd 是向量维度。Embedding 模型通过训练使“语义相近”的对象在向量空间中更接近。

数据库并不知道向量中的语言语义。数据库只负责:

  • 存储向量;
  • 计算距离或相似度;
  • 组织索引;
  • 执行过滤;
  • 返回候选结果。

Embedding 模型本身决定了“什么叫相似”。换模型、换版本或换预处理方式,可能改变整个检索空间,因此模型版本应作为数据契约管理。


10.2 距离度量

欧氏距离

d2(x,y)=i=1d(xiyi)2d_2(x,y) = \sqrt{\sum_{i=1}^{d}(x_i-y_i)^2}

距离越小,向量越接近。平方根不影响排序时,可以比较平方欧氏距离:

d22(x,y)=i=1d(xiyi)2d_2^2(x,y) = \sum_{i=1}^{d}(x_i-y_i)^2

点积

xy=i=1dxiyix \cdot y = \sum_{i=1}^{d}x_i y_i

若向量已经归一化,点积与余弦相似度相等。

余弦相似度

cos(x,y)=xyxycos(x,y) = \frac{x\cdot y}{\|x\|\|y\|}

其中:

x=i=1dxi2\|x\| = \sqrt{\sum_{i=1}^{d}x_i^2}

余弦距离常定义为:

dcos(x,y)=1cos(x,y)d_{cos}(x,y) = 1 - cos(x,y)

相似度越大越近,距离越小越近。工程中最常见的错误之一是把“距离升序”和“相似度降序”写反。


10.3 精确检索和近似检索

给定查询向量 qq,从 NN 个向量中返回距离最小的 kk 个:

TopK(q)=arg topk{d(q,xi)1iN}TopK(q) = \operatorname{arg\,top}_k \{ -d(q,x_i) \mid 1 \le i \le N \}

若逐个计算所有距离,这是精确检索,计算成本约为:

O(Nd)O(Nd)

其中 dd 是维度。

NN 很大时,常使用 ANN(Approximate Nearest Neighbor,近似最近邻)索引,以少量精度损失换取更低延迟。典型结构包括:

  • HNSW:多层图结构,通过邻居导航搜索;
  • IVF:先把向量划分到若干簇,只搜索部分簇;
  • PQ:对向量进行压缩,降低存储和距离计算成本。

近似检索的核心指标是召回率。若精确结果集合为 GkG_k,近似算法返回集合为 AkA_k,则可用:

Recall@k=GkAkkRecall@k = \frac{|G_k \cap A_k|}{k}

评估时需要用同一批查询比较精确基线和近似索引结果,不能只看平均延迟。


10.4 过滤和检索顺序

实际搜索通常不是“只找最相似的文本”,而是:

在 tenant_id = 10 且 language = 'zh' 的文档中
返回与查询向量最相似的 10 条

这涉及过滤和向量搜索的组合顺序:

  1. 先过滤再向量搜索:候选集合小,结果约束清晰;
  2. 先向量搜索再过滤:向量索引利用率高,但前若干候选可能都被过滤掉;
  3. 混合策略:扩大候选数,再过滤并补召回;
  4. 分区或分片:让租户、语言等条件与物理数据布局一致。

如果先取 10 个近邻,再过滤掉 9 个,只返回 1 个结果,这不是“索引不准”,而是候选数和过滤条件不匹配。扩大候选数可能改善召回,但会增加延迟,且不能保证任意过滤条件都有效。


10.5 一个可运行的 PostgreSQL 向量示例

下面示例假设 PostgreSQL 已安装向量扩展 pgvectorvector 类型、距离操作符和 HNSW 索引属于扩展能力,不是 PostgreSQL 核心 SQL 的内置语义。

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE document_chunk (
    chunk_id   bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    tenant_id  bigint NOT NULL,
    content    text NOT NULL,
    embedding  vector(3) NOT NULL,
    CHECK (vector_dims(embedding) = 3)
);

INSERT INTO document_chunk (tenant_id, content, embedding)
VALUES
    (10, 'PostgreSQL 支持事务和索引', '[0.90, 0.10, 0.00]'),
    (10, '数据库可以执行向量相似性搜索', '[0.80, 0.20, 0.00]'),
    (20, '分布式系统需要处理网络分区', '[0.00, 0.10, 0.90]');

使用余弦距离检索:

SELECT
    chunk_id,
    content,
    embedding <=> '[0.85, 0.15, 0.00]'::vector AS cosine_distance
FROM document_chunk
WHERE tenant_id = 10
ORDER BY embedding <=> '[0.85, 0.15, 0.00]'::vector
LIMIT 2;

这里:

  • <=> 表示余弦距离;
  • ORDER BY 必须升序,因为距离越小越相似;
  • tenant_id = 10 是结构化过滤;
  • 查询向量维度必须与 vector(3) 一致。

可建立 HNSW 索引:

CREATE INDEX document_chunk_embedding_hnsw_idx
ON document_chunk
USING hnsw (embedding vector_cosine_ops);

HNSW 是近似索引。它通常需要在数据写入后构建或维护,索引参数和扩展版本会影响构建成本、内存占用、延迟和召回率。生产环境应通过精确扫描结果建立测试基线,再调整索引和查询参数,而不是仅凭少量人工样例判断质量。


10.6 向量一致性与数据生命周期

向量检索至少涉及三种一致性:

内容与向量一致

文档内容修改后,旧 Embedding 仍可能留在库中。应通过事务状态或版本字段区分:

document.version = 8
embedding.version = 7

检索时不能把版本 7 当作版本 8 的向量使用。

主数据与索引一致

文档删除后,向量索引是否立即不可见,取决于存储系统和索引维护机制。逻辑删除可以快速隐藏结果,但会留下空间和维护成本;物理删除则可能需要后台重建或清理。

多副本读取一致

写入向量后,查询若被路由到延迟副本,可能暂时搜不到刚写入的数据。这与传统数据库的复制延迟本质相同,只是向量索引通常还有独立构建或刷新过程。

可靠流程可以是:

写入文档和状态 pending
  ↓
生成 embedding
  ↓
在同一数据系统中写入向量和模型版本
  ↓
确认索引可见
  ↓
状态改为 ready

如果 Embedding 生成在数据库事务外完成,就必须处理任务重复、超时、模型版本变化和旧任务覆盖新结果等问题。


十一、从单机到分布式、从精确到相似的取舍

可以把数据库能力分成两条扩展轴。

11.1 精确性轴

  • 主键、唯一约束、外键;
  • 事务和锁;
  • 可重复读、串行化;
  • 同步复制;
  • 跨分片事务。

越靠近强一致一端,通常越需要协调和等待,但这不是简单的“越强越好”。支付扣款需要强约束,日志搜索或推荐候选可能允许短暂不一致。

11.2 搜索轴

  • 等值查询;
  • 范围查询;
  • 排序和聚合;
  • 全文检索;
  • 向量相似性;
  • 结构化过滤与向量召回的混合查询。

向量检索不是关系查询的替代品。用户、租户、权限、时间范围和状态仍然适合用结构化字段表达;向量只负责表达模型空间中的相似关系。


十二、建议的完整学习路线

阶段一:关系模型和 SQL 基础

掌握:

  • 关系、元组、属性、域;
  • 主键、候选键、外键;
  • NULL 和三值逻辑;
  • SELECT、连接、聚合、子查询、窗口函数;
  • 约束与事务边界。

练习目标:设计用户、订单、商品、订单明细四张表,并说明每个字段的业务含义、候选键和删除策略。

阶段二:函数依赖和模式设计

掌握:

  • 函数依赖;
  • 部分依赖和传递依赖;
  • 第一、第二、第三范式;
  • 反规范化的语义边界;
  • 数据异常的构造与修复。

练习目标:给出一个故意重复的订单表,分别演示更新异常、插入异常和删除异常,再拆分并恢复查询。

阶段三:执行计划和索引

掌握:

  • B-tree 和复合索引;
  • 最左前缀;
  • 选择性与统计信息;
  • 排序、连接、聚合的执行方式;
  • EXPLAINEXPLAIN ANALYZE
  • 索引维护和写放大。

练习目标:对同一查询分别使用无索引、单列索引和复合索引,比较估算行数、实际行数、扫描方式和缓冲区访问。

阶段四:事务和并发控制

掌握:

  • ACID 的实际边界;
  • MVCC、锁和 WAL;
  • 隔离级别;
  • 脏读、不可重复读、幻读和写偏差;
  • 死锁、超时和序列化失败;
  • 事务重试与幂等。

练习目标:用两个数据库会话重现库存超卖、死锁和写偏差,再分别用行锁、约束和 SERIALIZABLE 修复。

阶段五:复制、分片和分布式事务

掌握:

  • 主副本和读副本;
  • 异步、同步和法定人数确认;
  • 复制延迟与读写一致性;
  • 分片键与热点;
  • 跨分片查询;
  • 2PC、Outbox 和 Saga 的边界;
  • 网络分区与故障恢复。

练习目标:设计一个按租户分片的订单系统,明确单租户事务、跨租户统计、主节点故障、重复消息和副本落后的处理方式。

阶段六:向量检索

掌握:

  • Embedding 的生成和版本化;
  • 欧氏距离、点积、余弦相似度;
  • 精确检索与 ANN;
  • HNSW、IVF、PQ 的基本思想;
  • Recall@k 和延迟评估;
  • 结构化过滤、权限过滤和候选扩展;
  • 文档、向量、索引和副本之间的一致性。

练习目标:用同一批查询比较精确扫描与近似索引,记录召回率、延迟和过滤后的有效结果数。


十三、最终应形成的判断能力

完成这条路线后,面对一个数据库问题,至少应先问:

  1. 这是数据建模问题,还是查询性能问题?
  2. 约束应由数据库、事务逻辑还是异步流程保证?
  3. 查询需要精确结果,还是允许近似召回?
  4. 当前异常是隔离级别导致、索引导致,还是复制延迟导致?
  5. 多节点是为了容量、可用性、吞吐,还是故障域隔离?
  6. 发生失败后,系统能否识别“未执行、执行中、已提交但响应丢失”三种状态?
  7. 任何性能优化是否改变了数据语义、历史快照或权限边界?

数据库的核心不是某个产品的命令集合,而是对状态、约束、访问路径、并发交错和故障恢复建立可验证的模型。关系模型保证事实表达清晰,事务保证状态变化可控,索引降低访问代价,分布式机制处理节点和网络故障,向量检索则把“相似”纳入可计算的查询语义。掌握这些层次之间的因果关系,才算真正建立了数据库基础体系。

完整学习目录

一、数据库共同原理

  1. 关系模型与规范化:键、函数依赖、范式和反规范化边界
  2. SQL 完整基础:查询逻辑顺序、连接、子查询、窗口函数与集合运算
  3. 数据库 Schema 设计:数据类型、主外键、约束、NULL 与演进
  4. 数据库索引原理:B+Tree、联合索引、覆盖索引与写放大
  5. SQL 执行计划与查询优化:基数估计、Join 算法和慢查询诊断
  6. 数据库事务完整指南:ACID、隔离级别、异常现象与正确边界
  7. MVCC、锁与死锁:可见性、锁粒度、等待图和线上诊断
  8. 数据库连接与连接池:容量、超时、排队、泄漏和故障恢复
  9. 数据库迁移与在线变更:Expand-Contract、锁风险和可回滚发布
  10. 数据库备份与恢复:全量、增量、PITR、RPO/RTO 和恢复演练
  11. 数据库复制、分片与高可用:一致性、路由、故障转移和扩容
  12. 数据库安全治理:最小权限、加密、审计、脱敏与注入防护

二、MySQL

  1. MySQL 架构全景:连接层、优化器、执行器、存储引擎与日志
  2. MySQL InnoDB 存储与索引:页、聚簇索引、回表和 Buffer Pool
  3. MySQL 事务与锁:Read View、间隙锁、死锁和一致性读
  4. MySQL 查询优化:EXPLAIN、统计信息、Join、排序和慢日志
  5. MySQL 复制与高可用:Binlog、GTID、半同步、切换与一致性
  6. MySQL 生产运维:参数、容量、备份、监控与常见故障排查

三、PostgreSQL

  1. PostgreSQL 架构全景:进程模型、共享内存、WAL 与存储布局
  2. PostgreSQL 数据类型与 Schema:数组、范围、JSONB、约束和域
  3. PostgreSQL MVCC 与 Vacuum:Tuple 可见性、膨胀、冻结和诊断
  4. PostgreSQL 索引与优化器:B-tree、GIN、GiST、BRIN 和执行计划
  5. PostgreSQL 复制与高可用:流复制、复制槽、PITR 和故障切换
  6. PostgreSQL JSONB 与扩展生态:全文检索、分区、FDW 和扩展边界

四、Oracle

  1. Oracle 数据库架构:Instance、SGA、PGA、数据文件与后台进程
  2. Oracle SQL 与 PL/SQL:数据类型、包、过程、异常和批处理
  3. Oracle 事务与 Undo:读一致性、SCN、锁、Redo 和恢复机制
  4. Oracle 索引与优化器:统计信息、执行计划、Hint 和 SQL 调优
  5. Oracle RMAN、Data Guard 与 RAC:备份恢复和高可用边界

五、SQLite

  1. SQLite 架构与文件格式:嵌入式数据库、Pager、B-tree 和 VFS
  2. SQLite 事务与 WAL:锁状态、并发读写、Checkpoint 和忙等待
  3. SQLite 查询与索引:类型亲和性、查询计划、FTS5 和性能边界
  4. SQLite 工程实践:嵌入应用、备份、迁移、损坏恢复与安全

六、Redis

  1. Redis 数据结构完整指南:String、Hash、List、Set、ZSet 与 Stream
  2. Redis 持久化与内存:RDB、AOF、过期淘汰、Fork 和恢复
  3. Redis 复制、Sentinel 与 Cluster:槽位、故障转移和一致性
  4. Redis 缓存体系:一致性、穿透、击穿、雪崩和多级缓存
  5. Redis 分布式协调:锁、Lua、限流、Pub/Sub 与 Streams

七、Elasticsearch

  1. Elasticsearch Mapping 与分词:字段类型、Analyzer 和索引设计
  2. Elasticsearch 查询与聚合:Query DSL、相关性、分页和统计
  3. Elasticsearch 分片与集群:路由、副本、恢复、容量和故障诊断
  4. Elasticsearch 数据写入与运维:Bulk、Ingest、ILM、快照和升级

八、其他主流数据库

  1. MongoDB 文档建模:嵌入与引用、Schema、事务和一致性
  2. MongoDB 索引与副本集:查询计划、分片、选举和生产运维
  3. ClickHouse 列式建模:MergeTree、排序键、分区和数据跳过
  4. ClickHouse 查询与运维:批量写入、物化视图、集群和性能诊断
  5. SQL Server 数据库引擎:存储、事务日志、锁、索引和执行计划
  6. SQL Server 高可用与运维:Backup、Always On、监控和故障恢复
  7. Cassandra 分布式数据模型:Partition Key、Clustering 与查询驱动设计
  8. Cassandra 一致性与运维:复制、Quorum、Compaction、Repair 和故障
  9. Neo4j 图数据建模:节点、关系、属性、约束与 Cypher 基础
  10. Neo4j 查询优化与运维:遍历、索引、执行计划、集群和备份
  11. InfluxDB 时序数据:时间模型、Schema、写入、查询和保留策略
  12. DuckDB 嵌入式分析:列式执行、Parquet、SQL 和本地数据工程
  13. TiDB 分布式 SQL:计算存储分离、Raft、事务与扩缩容
  14. OceanBase 分布式数据库:分区、副本、事务、高可用和运维
  15. MariaDB Server:与 MySQL 的差异、存储引擎、复制和迁移边界

九、向量数据库

  1. 向量数据库基础:Embedding、距离度量、召回、过滤与一致性
  2. 向量索引原理:Flat、HNSW、IVF、PQ 的精度、内存和延迟
  3. pgvector 实战:PostgreSQL 向量类型、HNSW、IVFFlat 与混合查询
  4. Milvus 完整基础:Collection、Segment、索引、查询和集群部署
  5. Qdrant 实战:Collection、Payload 过滤、HNSW、分片和快照
  6. Weaviate 基础:Collection、Vectorizer、过滤、混合检索和多租户
  7. Pinecone 托管向量库:Index、Namespace、Metadata 与容量成本
  8. 混合检索与 RAG 数据层:全文、向量、融合、重排和引用
  9. 向量数据库生产运维:摄取、版本、评测、备份、权限和成本

十、SQL 深入专题

  1. SQL 查询逻辑处理顺序:FROM、WHERE、GROUP、HAVING、SELECT 和 ORDER
  2. SQL Join 完整指南:Inner、Outer、Semi、Anti、算法和陷阱
  3. SQL 子查询与 CTE:相关子查询、递归、物化和优化边界
  4. SQL 窗口函数:分区、排序、Frame、排名、累计和间隔分析
  5. SQL 聚合与集合:GROUPING SETS、ROLLUP、CUBE、UNION 和去重
  6. SQL 数据写入:INSERT、UPDATE、DELETE、MERGE、Upsert 与并发正确性
  7. SQL 批处理与分页:游标、Keyset、批量写、限速和断点恢复
  8. 数据库协议与驱动:连接握手、预处理、结果流、取消和兼容性
  9. 数据库测试体系:事务测试、迁移测试、Testcontainers 和故障注入
  10. 数据库可观测性:连接、事务、锁、执行计划、复制和 SLO
  11. 数据库容量规划:工作集、缓存命中、IOPS、连接、增长和压测
  12. 多租户数据库设计:共享表、独立 Schema、独立库和数据隔离
  13. 时态数据与审计历史:有效时间、系统时间、版本表和可追溯性
  14. 数据库 CDC:日志捕获、Debezium、Schema 演进、顺序和重复消费
  15. OLTP、OLAP 与 Lakehouse:工作负载、存储布局和数据链路选型

十一、MySQL 深入

  1. MySQL 数据类型、字符集与排序规则:精度、编码和索引影响
  2. MySQL 表设计:主键、行格式、NULL、生成列、分区与归档
  3. MySQL Redo、Undo 与 Binlog:提交链路、崩溃恢复和一致性
  4. MySQL Buffer Pool 与内存结构:页缓存、刷脏、Change Buffer 和 AHI
  5. MySQL 分区表:Range、List、Hash、裁剪、维护和适用边界
  6. MySQL SQL 实战:分页、批量、Upsert、JSON、窗口函数和锁定读
  7. MySQL 备份恢复:逻辑备份、物理备份、Binlog 与 PITR 演练
  8. MySQL 用户、角色与安全:认证插件、权限、TLS、审计和密钥
  9. MySQL 协议与客户端:握手、Prepared Statement、连接池和取消
  10. MySQL 分片与 Vitess:路由、VSchema、重分片和跨分片事务
  11. MySQL 升级与迁移:兼容检查、DDL、字符集、灰度和回滚

十二、PostgreSQL 深入

  1. PostgreSQL 高级 SQL:LATERAL、递归 CTE、窗口、数组和范围
  2. PostgreSQL 存储内部:Heap Page、Tuple、TOAST、FSM 和可见性图
  3. PostgreSQL WAL 与 Checkpoint:提交、崩溃恢复、归档和写入性能
  4. PostgreSQL 锁与 Serializable SSI:谓词冲突、死锁和咨询锁
  5. PostgreSQL 分区表:规划、裁剪、索引、维护和在线迁移
  6. PostgreSQL 全文与模糊检索:tsvector、GIN、Trigram 和相关性
  7. PostgreSQL 逻辑复制与 CDC:Publication、Slot、顺序和 Schema
  8. PostgreSQL 备份恢复:pg_dump、Base Backup、WAL 归档和 PITR
  9. PostgreSQL 监控与诊断:pg_stat、锁、膨胀、慢查询和容量
  10. PostgreSQL 参数与性能:内存、WAL、Planner、连接和 Autovacuum
  11. PostgreSQL PostGIS:空间类型、坐标系、空间索引和查询优化

十三、Oracle 与 SQLite 深入

  1. Oracle Schema 与数据类型:NUMBER、字符、日期、LOB 和对象
  2. Oracle 存储管理:Tablespace、Segment、Extent、Block 和 ASM
  3. Oracle Redo、归档与 Checkpoint:提交、恢复和写入路径
  4. Oracle 锁与隔离:行锁、ITL、读一致性、死锁和诊断
  5. Oracle 分区与大表治理:Range、List、Hash、交换和裁剪
  6. Oracle 物化视图与查询重写:刷新、日志、一致性和性能
  7. Oracle 安全治理:用户、角色、Profile、TDE、审计和最小权限
  8. Oracle 监控与调优:AWR、ASH、等待事件、统计和容量
  9. Oracle Data Pump 与迁移:导出导入、字符集、校验和停机窗口
  10. SQLite 类型系统与 STRICT 表:亲和性、存储类、约束和兼容
  11. SQLite JSON、FTS5 与扩展:虚拟表、Tokenizer 和安全加载
  12. SQLite Backup API 与在线复制:快照、一致性、锁和恢复验证
  13. SQLite 安全边界:文件权限、注入、扩展、加密选择和不可信数据库

十四、Redis 与 Elasticsearch 深入

  1. Redis 内核与事件循环:命令执行、IO 线程、阻塞点和延迟
  2. Redis 对象编码与内存:SDS、Dict、Listpack、碎片和大 Key
  3. Redis Pipeline、事务与 Lua:原子性、脚本缓存和集群边界
  4. Redis 过期与 Keyspace 通知:惰性删除、主动删除、事件和可靠性
  5. Redis Search 与向量检索:索引、查询、Hybrid 和容量边界
  6. Redis 监控与延迟诊断:Slowlog、Latency、内存、复制和热点
  7. Lucene 与倒排索引内部:Segment、Term、Posting、Merge 和 Cache
  8. Elasticsearch 中文检索:分词器、词典、同义词、拼音和版本治理
  9. Elasticsearch 相关性调优:BM25、Boost、Function Score 和评测集
  10. Elasticsearch 深分页与一致性:search_after、PIT、Scroll 和导出
  11. Elasticsearch 安全与多租户:TLS、角色、文档权限、审计和隔离
  12. Elasticsearch 快照、恢复与升级:兼容、重建索引、灰度和回滚

十五、分布式与数据治理

  1. 分布式数据库一致性模型:线性一致、顺序一致、因果和最终一致
  2. 数据库共识基础:Raft、Paxos、Leader、日志复制和成员变更
  3. 分布式数据库中的时间与 ID:时钟、序列、Snowflake、UUID 和顺序
  4. 数据库代理与网关:连接复用、读写路由、分片、审计和故障边界
  5. 云托管数据库选型:责任边界、高可用、扩缩容、成本和退出策略
  6. 数据质量与治理:血缘、口径、约束、校验、责任人和变更审计
  7. 数据库隐私与数据生命周期:分类、最小化、脱敏、留存和删除
  8. ETL 与 ELT 数据管道:批流处理、幂等、补数、校验和可观测
  9. 数据契约与 Schema Registry:兼容模式、演进、验证和消费者治理
  10. 数据库故障演练:延迟、断网、磁盘满、主库切换和恢复验证

系列导航与关联阅读

官方资料

本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。