引言:数据库不只是存放数据的容器
文件、JSON、CSV 和电子表格都能保存数据,为什么软件系统还需要数据库?
因为“把数据写下来”只是问题的起点。只要数据需要被多个请求持续读取和修改,我们就必须继续回答:数据应该遵守什么结构?两个文件中的记录如何保持关联?两个人同时购买最后一件商品时该听谁的?程序写到一半崩溃后,哪些修改算成功?数据越来越多以后,怎样快速找到需要的记录?
数据库管理系统正是为这些问题而设计的。它不仅负责存储数据,还负责组织结构、执行查询、检查约束、协调并发、记录修改并在故障后恢复。
本文是数据库系列的开篇。我们不绑定 MySQL、PostgreSQL 等具体产品,也不深入某一种索引或事务实现,而是先建立一张完整的认知地图:
为什么需要数据库
↓
关系模型如何描述数据
↓
关系型数据库提供哪些核心能力
↓
数据库系统内部如何协作
↓
数据库为什么面临并发、性能、可靠性和扩展问题
↓
主流产品之间应该如何理解和选择
1. 为什么应用最终会需要数据库
在原型或个人工具中,把少量配置写进 JSON、把统计结果导出为 CSV,通常完全合理。问题不在于“文件不能保存数据”,而在于业务增长后,应用开始承担文件格式本身没有提供的管理责任。
以一个小型电商系统为例。最初,我们可以把用户、商品和订单分别保存在几个文件中。随着功能增加,很快会遇到下面的问题:
- 同一个用户的姓名和地址出现在多个订单文件中,修改资料时容易遗漏。
- 订单引用了一个已经删除的用户,但文件系统不会阻止这类非法引用。
- 查询某位用户最近一个月购买的商品,需要读取并组合多个文件。
- 两个请求同时读取库存为 1,又各自写回库存为 0,系统实际上卖出了两件商品。
- 创建订单时,订单文件已经写入,库存文件却写入失败,业务停在“订单存在但库存未扣减”的中间状态。
- 程序在覆盖文件的过程中崩溃,应用需要自行判断文件是否完整以及如何恢复。
- 权限、审计、备份、迁移和数据演进逐渐变成一套新的基础设施。
当然,开发者可以继续在应用层解决这些问题:设计文件锁、维护索引、编写恢复脚本、实现权限检查。但是,当这些能力成为许多系统的共同需求时,将它们集中到一个经过长期验证的数据管理系统中更合理。

因此,数据库的核心价值不是“比文件更高级”,而是管理长期存在、持续变化、被并发共享的数据状态。关系型数据库则是在这种需求上,以关系模型作为组织数据的基础。
2. 什么是关系型数据库
讨论关系型数据库时,最好先区分三个层次:
- 关系模型(Relational Model) 是一套描述数据结构、完整性约束和数据操作的理论模型。
- 关系型数据库(Relational Database) 是按照关系模型组织的数据集合。
- 关系型数据库管理系统(RDBMS) 是创建、查询、修改、保护和恢复这些数据的软件系统,例如 PostgreSQL、MySQL 和 SQLite。
日常交流中,人们常把“关系型数据库”和“RDBMS”混用,这通常不影响沟通。但在理解原理时,区分模型、数据与管理软件很重要:表和键来自数据模型,解析器、优化器、缓存与恢复机制则属于数据库系统的工程实现。
可以把关系型数据库概括为:
关系型数据库以关系模型为基础,将数据表示为若干关系,并通过键、约束和关系运算组织、关联与查询数据;RDBMS 则负责让这些操作在并发和故障环境中可靠执行。
这里最容易误解的是“关系”一词。关系型数据库中的 Relation 首先是数学意义上的关系,不是“两个业务对象之间存在联系”的简称。用户与订单之间的业务联系叫 Relationship,它可以借助键和中间关系表达,但两者不是同一个概念。
3. 从现实世界到关系模型
现实世界中有用户、商品、订单等对象。数据建模的任务,是选择哪些事实需要记录,以及用什么结构表达这些事实。
在电商示例中,我们可以得到四个关系:
users:用户是谁。products:系统销售哪些商品。orders:某位用户创建了哪张订单。order_items:一张订单包含哪些商品、数量和成交单价。

3.1 关系、元组、属性与域
关系(Relation) 通常表现为一张表。它描述同一类事实,例如 products 关系描述商品。
元组(Tuple) 通常表现为表中的一行。一行商品数据表示一个具体商品。
属性(Attribute) 通常表现为一列,例如商品的 name、price 和 stock。
域(Domain) 表示属性允许取值的集合。商品价格可以被定义为非负的小数,库存可以被定义为非负整数。数据库中的数据类型和 CHECK 等约束,都是表达域规则的常用方式。
这里还有两个经常被表格界面掩盖的事实:
- 关系中的元组没有天然顺序。SQL 查询如果没有显式使用
ORDER BY,数据库不承诺结果按主键、插入时间或磁盘位置排列。 - 纯关系模型中的关系是元组集合,不包含重复元组;SQL 实现通常采用更接近多重集合的语义,查询结果可能重复,因此需要键、
DISTINCT或其他约束表达唯一性。
3.2 键与实体之间的联系
候选键(Candidate Key) 是能够唯一标识一行的最小属性集合。一张表可能有多个候选键,例如用户的内部编号和经过验证的唯一邮箱都可能具备唯一性。
主键(Primary Key) 是被选作主要标识的候选键。主键的意义是稳定地标识事实,不等于“必须使用自增整数”。
外键(Foreign Key) 声明一列或多列引用另一个关系中的候选键,用来维护引用完整性。例如 orders.user_id 引用 users.id,数据库便可以拒绝属于不存在用户的订单。
用户与订单是一对多:一个用户可以有多张订单,一张订单属于一个用户。订单与商品则是多对多:一张订单可以包含多个商品,同一个商品也可以出现在多张订单中。order_items 把这个多对多联系展开成两个一对多联系,并同时记录数量和成交单价。
下面是一组简化的表定义。示例采用接近 SQL 标准的写法,不同数据库对自增列、时间类型和标识符的语法可能略有差异:
CREATE TABLE users (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
email VARCHAR(320) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL
);
CREATE TABLE products (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price NUMERIC(12, 2) NOT NULL CHECK (price >= 0),
stock INTEGER NOT NULL CHECK (stock >= 0)
);
CREATE TABLE orders (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
status VARCHAR(20) NOT NULL
CHECK (status IN ('pending', 'paid', 'cancelled')),
created_at TIMESTAMP NOT NULL
);
CREATE TABLE order_items (
order_id BIGINT NOT NULL REFERENCES orders(id),
product_id BIGINT NOT NULL REFERENCES products(id),
quantity INTEGER NOT NULL CHECK (quantity > 0),
unit_price NUMERIC(12, 2) NOT NULL CHECK (unit_price >= 0),
PRIMARY KEY (order_id, product_id)
);
这段定义不仅创建了四个“容器”,还声明了数据之间必须长期成立的规则。数据库可以利用这些规则阻止一部分非法状态,而不必相信每一个调用方都正确实现了相同检查。
4. 表、键、约束与规范化
Schema 不只是列名清单,它是数据库对数据形状和有效状态的正式约定。
NOT NULL表示某个事实不可缺失。UNIQUE防止本应唯一的值重复。CHECK限定单行数据必须满足的条件。PRIMARY KEY提供稳定且唯一的行标识。FOREIGN KEY保证引用目标存在,并规定目标被更新或删除时的行为。- 数据类型限定值的基本表示范围。
例如,下列写入会失败,因为订单引用的用户不存在:
INSERT INTO orders (id, user_id, status, created_at)
VALUES (10001, 99999, 'pending', CURRENT_TIMESTAMP);
但数据库约束不是全部业务规则。数据库很容易检查 quantity > 0,却未必知道“新用户首单最多使用一张新人券”或“特定地区周末暂停配送”。复杂规则可能由数据库约束、事务、应用服务和跨系统协议共同维护。好的设计不是把所有规则都塞进数据库,而是让关键不变量在最可靠、最接近数据的位置得到保护。
4.1 为什么需要规范化
假设每张订单都完整复制用户姓名、邮箱和当前地址。当用户修改邮箱时,历史订单中的多份副本可能只更新了一部分,系统便无法回答哪一份才是权威数据。这类问题称为更新异常;类似地,还可能出现插入异常和删除异常。
规范化(Normalization)通过识别不同事实的依赖关系,将职责不同的数据放进合适的关系,以减少没有必要的重复和修改异常。在当前模型中:
- 用户资料的权威值保存在
users。 - 商品当前价格和库存保存在
products。 - 订单属于哪个用户保存在
orders。 - 购买数量和成交时单价保存在
order_items。
order_items.unit_price 与 products.price 看起来重复,却表达不同事实:前者是成交时的历史价格,后者是商品当前价格。保留历史快照不是随意冗余,而是业务语义的一部分。
第一范式、第二范式和第三范式提供了逐步分析数据依赖的方法,但规范化不是“表拆得越多越好”。报表、搜索和高频读取有时会有意识地保存派生结果,这叫反规范化。关键是明确权威来源、同步方式和允许的不一致窗口,而不是机械地追求零重复。
5. 关系型数据库的核心能力
关系模型回答数据如何表达,RDBMS 还必须提供一组让数据真正可用的能力。

5.1 声明式查询
SQL 的重要特征是声明式:使用者描述想得到什么,数据库决定怎样得到。下面的查询表达“找出某位用户最近创建的订单及其商品”,却没有指定先读哪张表、使用哪种 Join 算法或是否使用索引:
SELECT
o.id AS order_id,
o.created_at,
p.name AS product_name,
oi.quantity,
oi.unit_price
FROM orders AS o
JOIN order_items AS oi ON oi.order_id = o.id
JOIN products AS p ON p.id = oi.product_id
WHERE o.user_id = 42
ORDER BY o.created_at DESC;
数据库会结合 SQL 结构、可用索引和统计信息生成执行计划。数据分布或索引变化后,同一条 SQL 可能使用不同计划,而应用不必重写成另一套逐行读取流程。
5.2 完整性约束
约束是数据库能够理解并强制执行的数据规则。它们的价值不只在于提前报错,还在于形成统一边界:无论写入来自 Web 服务、批处理脚本还是管理工具,都必须满足相同规则。
约束也能为开发者提供明确反馈。重复邮箱会触发唯一约束,负数库存会触发检查约束,无效用户会触发外键约束。相比让错误数据悄悄进入系统,在写入边界失败通常更容易定位和修复。
5.3 事务与 ACID
创建订单往往包含多项修改:写入订单、写入明细、扣减库存。这些语句在业务上属于一个工作单元,任何一步失败都不应该留下半完成状态。
BEGIN;
INSERT INTO orders (id, user_id, status, created_at)
VALUES (10001, 42, 'pending', CURRENT_TIMESTAMP);
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
VALUES (10001, 7, 2, 199.00);
UPDATE products
SET stock = stock - 2
WHERE id = 7 AND stock >= 2;
-- 以下两条由应用根据 UPDATE 的影响行数二选一:
-- COMMIT; -- 影响行数等于 1
-- ROLLBACK; -- 影响行数等于 0
实际应用还必须检查 UPDATE 是否真的更新了一行;如果库存不足,应回滚而不是继续提交。事务不会替应用自动推导业务成功条件。
ACID 描述事务通常需要具备的四类性质:
- Atomicity(原子性) :事务中的修改作为整体提交或回滚,避免只留下部分结果。
- Consistency(一致性) :事务在数据库约束与应用不变量共同作用下,从一个有效状态进入另一个有效状态。
- Isolation(隔离性) :并发事务之间可以看到什么,由隔离级别和具体实现控制;它不是“并发事务完全互不影响”的无条件承诺。
- Durability(持久性) :提交结果能够在数据库及其部署方案承诺的故障范围内恢复。
因此,“使用了事务”不等于业务必然正确。应用仍要划分合理的事务边界、检查影响行数、选择隔离级别,并处理锁等待、死锁、序列化失败和重试。
5.4 索引与高效访问
没有合适索引时,数据库可能需要检查大量行才能找到少量结果。索引维护从键值到数据位置的辅助结构,使数据库有机会缩小访问范围。
例如,系统经常按用户和时间查询订单,可以考虑在 orders(user_id, created_at) 上建立联合索引。但索引不是免费的:它占用空间,每次插入、更新和删除都可能需要同步维护;低选择性条件或返回大量行的查询,也不一定适合索引扫描。
“为查询建立索引”只是起点。真正的判断需要结合执行计划、数据分布和真实负载,这会在索引与查询优化专题中展开。
5.5 安全、权限与审计
关系型数据库通常提供身份认证、用户与角色、对象权限和审计能力。权限可以限制某个角色能否连接数据库、访问指定 Schema,或者对某张表执行 SELECT、INSERT、UPDATE 和 DELETE。部分产品还提供列级或行级访问控制。
数据库权限不是应用权限系统的替代品。应用仍然需要判断“当前用户能否修改这张订单”等业务规则;数据库则负责限制连接它的账号可以触及哪些数据和操作。两层边界共同存在,可以降低单个应用缺陷直接扩大为全库越权的风险。
6. 关系型数据库系统的通用架构
一条 SQL 背后并不是一个单独的“读表函数”,而是一组协作模块。

这张图是一种通用认知模型,不代表所有产品都有完全相同的进程、线程或模块边界。PostgreSQL、MySQL 等客户端—服务器数据库通常由独立服务进程管理连接和存储;SQLite 则嵌入应用进程,直接读写数据库文件。它们仍然需要处理 SQL、事务和存储,只是部署形态与内部边界不同。
6.1 连接与会话
客户端首先通过驱动或协议建立连接。数据库在会话中维护身份、权限、事务状态、参数和临时对象等上下文。连接池可以复用连接,但不会消除数据库对并发会话的资源管理。
6.2 解析、规划与执行
解析器检查 SQL 语法并形成内部结构;语义分析确认表、列、类型和权限是否有效。优化器再枚举或比较候选方案,例如先访问哪张表、使用顺序扫描还是索引扫描、采用哪种 Join 方法,并依据统计信息估算成本。
执行器按照计划调用扫描、过滤、连接、聚合和排序等算子。优化器选择的是预计成本较低的计划,不是对所有运行时情况都绝对最快的计划;统计信息过期、参数分布倾斜或估算模型有限,都可能导致误判。
6.3 事务、并发与恢复
事务管理并不是 SQL 处理完成后的单独步骤,它贯穿读取和写入。数据库需要判断当前事务能看到哪些版本、写入是否与其他事务冲突、何时等待或回滚,以及提交前哪些日志必须持久化。
锁和 MVCC 是常见机制,但两者不是互相替代的单选项。许多数据库同时使用多版本数据支持并发读取,又使用不同粒度的锁保护结构或协调写冲突。隔离级别决定允许哪些并发现象,恢复机制则负责在进程或机器故障后重建一致状态。
6.4 缓冲区、数据页、索引与日志
数据库通常以页为单位组织磁盘数据,并在内存缓冲区中缓存热点页。索引提供额外的访问路径,日志以适合恢复的形式记录修改。使用预写日志时,描述修改的日志需要先于相关数据页达到持久化要求。故障恢复时,数据库可以根据日志重放尚未写入数据页的修改,并结合事务状态或 Undo 等机制识别和处理未完成事务;具体流程因产品而异。
缓存提高了性能,也使“事务提交”和“所有数据页立刻写回磁盘”不再是同一件事。检查点、日志刷新策略、存储设备保证和副本配置共同决定性能与故障边界。
6.5 从一条 SQL 看模块如何协作
以下查询用于查看用户最近十张订单:
SELECT id, status, created_at
FROM orders
WHERE user_id = 42
ORDER BY created_at DESC
FETCH FIRST 10 ROWS ONLY;
这里使用接近 SQL 标准的限制行数写法;MySQL、SQLite 和 SQL Server 等产品可能使用 LIMIT、TOP 或 OFFSET ... FETCH 等不同语法。
它在数据库内部大致经历以下过程:
- 连接层接收 SQL,并确认当前会话是否有查询权限。
- 解析与语义分析识别
orders、列名、条件和排序要求。 - 优化器结合统计信息和索引,选择扫描、过滤与排序方案。
- 执行器启动计划中的算子,并向事务系统请求当前语句可见的数据版本。
- 存储层从缓冲区取得数据页;未命中时再读取持久化存储。
- 执行器完成过滤、排序或索引有序读取,将前十行返回客户端。
这条路径足以把架构模块串起来,但还没有回答优化器如何估算成本、B+Tree 如何定位页、MVCC 如何判断可见性等问题。它们值得分别展开,而不是挤进一篇开篇文章。
7. 数据库系统面对的核心技术挑战
数据库的许多复杂机制,都可以追溯到四类工程问题:并发、性能、可靠性和扩展性。

7.1 并发:正确性与吞吐量之间的平衡
两个事务同时修改同一商品库存,可能产生丢失更新;两个转账事务以不同顺序锁定账户,可能互相等待形成死锁;一个事务连续读取两次,也可能在另一个事务提交后看到不同结果。
数据库使用锁、MVCC、隔离级别、冲突检测和事务重试等机制控制这些现象。更严格的隔离通常减少应用能够观察到的并发异常,但可能增加锁等待、版本维护、冲突回滚或重试成本。正确做法不是一律选择最强或最弱隔离,而是先确定业务不变量,再选择能够保护它的机制。
7.2 性能:不存在对所有查询都最优的结构
顺序扫描适合读取表中很大比例的数据,索引扫描适合定位较少记录;嵌套循环、哈希连接和归并连接各有适用条件。数据库需要根据统计信息估算行数、I/O 和 CPU 成本,再选择计划。
索引可以加速读取,却增加写放大和存储占用;缓存减少磁盘访问,却受到内存容量和淘汰策略限制;反规范化减少 Join,却提高同步复杂度。性能优化本质上是针对真实访问模式做权衡,应使用 EXPLAIN、慢查询记录和生产负载证据,而不是套用“有索引就快”的规则。
7.3 可靠性:日志、备份与复制解决不同问题
进程崩溃、操作系统故障、磁盘损坏、误删除和机房故障不是同一种风险。预写日志和崩溃恢复主要帮助数据库恢复事务状态;检查点控制恢复所需处理的日志范围;副本可以提高可用性或提供冗余;备份与时间点恢复则用于应对数据损坏、误操作和更长时间范围的恢复需求。
复制不能替代备份:误删除可能被忠实地复制到所有副本。备份也不等于已经能够恢复:只有定期执行恢复演练,才能验证备份文件、密钥、日志链和操作流程确实可用。
7.4 扩展性:把数据分开会引入新的协调成本
单机可以通过更快的 CPU、更多内存和更好的存储纵向扩展,也可以通过读副本分担部分读取。表分区把一张逻辑表拆成多个分区,便于裁剪扫描范围和管理数据;分片则把数据分布到多个独立节点,扩大容量或写吞吐。
这些概念不能混为一谈。分区不一定跨机器,读副本通常不能扩展单主写入,分片则会让跨分片事务、全局唯一性、Join、再均衡和故障处理变复杂。分布式关系型数据库尝试把一部分协调封装进系统,但网络延迟、一致性和可用性的取舍不会因此消失。
8. 主流关系型数据库生态
关系模型是一套共同基础,不等于某个具体产品。不同 RDBMS 在许可证、部署形态、扩展机制、工具链和兼容语法上各有选择。

| 产品 | 可核验的基本定位 | 选型时需要继续确认 |
|---|---|---|
| PostgreSQL | 开源 RDBMS,强调可扩展性并持续支持 SQL 标准能力 | 扩展、托管方案、团队运维经验 |
| MySQL | 提供开源 Community Edition,广泛用于 Web 与通用事务应用 | 版本、存储引擎、发行版和商业支持 |
| SQLite | 嵌入应用进程、无独立数据库服务、主数据库通常是单文件 | 并发写入、网络共享和服务端访问需求 |
| Oracle Database | 商业数据库产品,提供集成的数据管理与运维能力 | 许可证、版本功能、现有 Oracle 技术栈 |
| SQL Server | 商业 RDBMS,使用 T-SQL,可部署在 Windows、Linux、容器和 Azure 等环境 | 版本授权、Microsoft 数据平台集成 |
| MariaDB | 开源 RDBMS,源自 MySQL,保留较高生态兼容性但已独立演进 | 具体版本的 SQL、复制、驱动和工具兼容性 |
这张表不是产品排名,也不表示某个产品只能用于所列场景。“企业级”“高可靠”“生态成熟”如果没有版本、部署和衡量指标,只是无法比较的口号。选型时应把问题具体化:系统部署在哪里?需要哪些 SQL 和事务能力?团队熟悉什么?许可证和支持成本是多少?如何备份、升级和监控?现有驱动、ORM 与数据平台能否兼容?
产品也在持续演进。同一个名称之下可能有社区版、商业版、云托管版和不同发布周期,最终决定必须依据目标版本的官方文档和实际验证。
9. 关系型数据库与 NoSQL 的关系
NoSQL 不是一个具有统一数据模型和一致性语义的产品类别。键值、文档、宽列和图数据库解决的问题并不相同,因此“关系型数据库还是 NoSQL”通常只是讨论的开头。
| 对比维度 | 关系型数据库 | 常见 NoSQL 数据库 |
|---|---|---|
| 数据模型 | 关系、键、约束与关系运算 | 键值、文档、宽列、图等不同模型 |
| Schema | 通常显式定义并通过迁移演进 | 常允许更灵活的记录结构,也可能提供 Schema 校验 |
| 关联查询 | SQL、Join 和聚合是核心能力 | 常围绕聚合边界或特定访问路径设计,能力因产品而异 |
| 事务与一致性 | 普遍提供事务和多种隔离级别 | 支持范围、一致性选项和事务语义因产品而异 |
| 扩展方式 | 单机、复制、分区、分片或分布式产品 | 许多产品从特定模型出发设计水平扩展 |
| 主要代价 | Schema 演进、Join 和分布式协调需要设计 | 跨记录约束、临时查询或多模型组合可能更依赖应用 |
“Schema 灵活”不等于没有 Schema:数据结构仍然存在,只是约束可能从数据库转移到应用、序列化格式或校验层。“支持水平扩展”也不是 NoSQL 独占能力,许多关系型产品同样支持复制、分片或分布式部署,只是具体语义和成本不同。
真实系统经常采用多种存储:关系型数据库保存订单和账户等核心状态,Redis 承担缓存或特定实时结构,搜索引擎建立全文检索索引,对象存储保存大文件。合理的多存储架构不是“每种数据库都用一点”,而是为每份数据明确权威来源、同步方式和故障处理策略。
10. 为什么关键业务仍大量依赖关系型数据库
订单、账户、库存、权限和工作流有一个共同特点:数据之间存在明确关联,错误状态需要被尽早阻止,多项修改经常需要作为一个工作单元提交,而且数据会经历多年的查询、审计和结构演进。
关系模型、约束、事务和声明式查询恰好为这些需求提供了通用基础。它们让系统能够明确表达“什么数据存在”“数据之间如何关联”“哪些状态不允许出现”,也让后来的查询不必完全受最初写入接口限制。
这不意味着所有数据都应该放进同一个关系型数据库。缓存、消息系统、搜索引擎和对象存储仍有各自价值。关键是让关系型数据库承担它擅长的共享业务状态,并明确其他组件与权威数据之间的边界。
11. 总结:关系型数据库究竟是什么
关系型数据库不是“用表格保存数据的软件”。更准确地说,它以关系模型表达事实,通过键和约束定义有效状态,通过 SQL 声明查询与修改,再由数据库管理系统使用事务、并发控制、索引、缓存、日志和恢复机制,让这些操作能够在真实工程环境中执行。
可以用五句话回顾全文:
- 关系模型定义数据如何表示,Relation 不等于实体之间的 Relationship。
- 键和约束让一部分业务不变量成为所有写入者共同遵守的数据边界。
- SQL 描述目标,优化器和执行器决定具体访问路径。
- 事务、锁、MVCC、日志与恢复共同处理并发和故障,但每种保证都有边界与成本。
- PostgreSQL、MySQL、SQLite 等产品共享关系模型基础,却在部署、功能、许可证和工程实现上各有选择。
后续文章将继续深入 SQL 执行、索引、事务、并发控制、恢复与分布式数据库等主题。
参考资料
- PostgreSQL Documentation: Concepts
- PostgreSQL Documentation: Constraints
- PostgreSQL Tutorial: Transactions
- PostgreSQL Documentation: Transaction Isolation
- PostgreSQL Documentation: Using EXPLAIN
- PostgreSQL Documentation: WAL Internals
- SQLite Is Serverless
- SQLite: Single-file Cross-platform Database
- MySQL Community Edition
- Microsoft Learn: What is SQL Server?
- MariaDB Server in brief
- Oracle: What Is a Relational Database?