ACID、隔离级别与 SSI

共 40 题
📑 题目列表 40 题
#
★★★

1. ACID(原子性、一致性、隔离性、持久性)的精确语义与实现机制?

请说明 ACID(原子性、一致性、隔离性、持久性)的精确语义,以及数据库如何实现它们?

  • 原子性:事务要么全部成功要么全部回滚。
  • 一致性:事务前后数据满足约束。
  • 隔离性:并发事务互不干扰。

ACID 是事务的四大特性。原子性(Atomicity):事务中的所有操作要么全部提交要么全部回滚,不能部分成功,通过 undo log(回滚日志)或 WAL 中的回滚机制实现。一致性(Consistency):事务执行前后数据库都满足约束(主键、外键、唯一、CHECK 等),由应用层与数据库约束共同保证,原子性、隔离性、持久性合力维护一致性。隔离性(Isolation):并发事务之间相互隔离,通过锁、MVCC 等实现。持久性(Durability):事务提交后其修改永久保存,即使崩溃也不丢失,通过 WAL 与 fsync 落盘实现。四个特性相互依赖:原子性靠 undo、持久性靠 redo、隔离性靠锁/MVCC,一致性由约束与前三者共同保证。

理解 ACID 需要对应到具体机制:原子性-undo、持久性-redo/WAL、隔离性-锁/MVCC、一致性-约束。这是事务理论的核心。

#
★★★

2. 事务的可见性边界,transaction_isolation、session 级别设置?

请说明事务可见性边界如何受隔离级别控制,以及 session 级别设置隔离级别的作用?

  • transaction_isolation 参数控制事务隔离级别。
  • session 级别设置影响当前会话事务。
  • 可见性边界(快照)由隔离级别决定。

事务的可见性边界由隔离级别决定。PostgreSQL 通过 SET transaction_isolation = 'READ COMMITTED' 等设置会话或事务的隔离级别;MySQL 通过 SET SESSION TRANSACTION ISOLATION LEVEL ...SET TRANSACTION ISOLATION LEVEL ... 设置。隔离级别决定事务读取数据时可见哪些版本:READ COMMITTED 每条语句建立新快照(可见已提交数据),REPEATABLE READ 事务开始时建立快照(全事务可见一致),SERIALIZABLE 还需检测写冲突。session 级别设置适用于当前会话的所有事务,事务级别设置仅作用于当前事务。可见性边界即"快照"的边界,直接决定不可重复读、幻读等异常是否出现。

transaction_isolation 与 session 级设置共同决定事务的快照边界。理解"快照创建时机"与隔离级别的关系是核心。

#
★★★

3. 事务边界(BEGIN、COMMIT、ROLLBACK)的实现细节,PostgreSQL 的事务 ID、MySQL 的 trx_id?

请说明 BEGIN、COMMIT、ROLLBACK 的实现细节,以及 PostgreSQL 事务 ID 与 MySQL trx_id 的作用?

  • BEGIN 开启事务,COMMIT 提交,ROLLBACK 回滚。
  • PostgreSQL 用事务 ID(xid)标记事务。
  • MySQL 用 trx_id 标记事务,用于 MVCC。

事务边界由 BEGIN、COMMIT、ROLLBACK 语句界定。BEGIN/START TRANSACTION 开启事务,COMMIT 提交所有修改,ROLLBACK 撤销所有修改。实现细节上,PostgreSQL 为每个事务分配递增的事务 ID(xid),每行的 xmin/xmax 记录创建/删除该行的事务 ID,通过比较 xid 与快照判定可见性;COMMIT 后写 WAL 并更新事务状态。MySQL InnoDB 为每个事务分配自增的 trx_id,每行记录 DB_TRX_ID(创建该版本的事务 ID),undo log 保留旧版本,通过 trx_id 与 Read View(活跃事务集合)判定可见性。事务 ID 是 MVCC 可见性判定的核心,也是事务边界实现的基础。

事务边界 + 事务 ID(xid/trx_id)构成 MVCC 的基础。理解 COMMIT/ROLLBACK 如何更新事务状态与 WAL 是核心。

#
★★★

4. 嵌套事务(Nested Transaction)与保存点(SAVEPOINT)的差异?

请说明嵌套事务与保存点(SAVEPOINT)的差异,以及数据库如何实现?

  • 数据库通常不支持真正的嵌套事务。
  • SAVEPOINT 提供事务内部分回滚能力。
  • 内部回滚不影响外部提交。

大多数关系数据库(PostgreSQL、MySQL、Oracle)不支持真正的嵌套事务(真正的嵌套事务指外部可回滚内部、内部提交不影响外部),而是通过 SAVEPOINT(保存点)模拟"事务内子事务"。SAVEPOINT 在事务内设置一个点,之后可 ROLLBACK TO SAVEPOINT 回滚到该点,撤销之后的操作,但不会回滚整个事务;之后可 RELEASE SAVEPOINT 释放保存点。因此 SAVEPOINT 提供"部分回滚"能力,而嵌套事务理论要求内部事务的提交/回滚独立于外部。数据库用 SAVEPOINT 实现近似嵌套事务的语义。理解差异:真正的嵌套事务是独立子事务,SAVEPOINT 是事务内的回滚标记。

关键区别是"独立子事务"(嵌套事务)vs "事务内回滚标记"(SAVEPOINT)。数据库用 SAVEPOINT 近似嵌套事务。

#
★★★

5. 长事务(Long Transaction)的危害,MVCC 版本膨胀、锁持有时间?

请说明长事务(Long Transaction)的危害,特别是 MVCC 版本膨胀与锁持有时间?

  • 长事务导致 MVCC 旧版本无法回收,版本膨胀。
  • 长事务长时间持有锁,增加冲突。
  • 影响 VACUUM、redo 空间等。

长事务(长时间未提交或未结束的事务)危害明显。一是 MVCC 版本膨胀:PostgreSQL 中,事务的 xmin 成为"最老活跃事务",VACUUM 无法清理该事务之后产生的旧版本,导致表和索引膨胀,占用大量空间;MySQL 中 undo log 无法被 purge,版本堆积。二是锁持有时间:长事务长期持有行锁或表锁,阻塞其他事务,增加锁等待与死锁概率。三是其他影响:redo/undo 日志增长、快照过旧、VACUUM 无法推进。治理手段:缩短事务、及时提交、设置 idle_in_transaction_session_timeout、监控长事务并告警。

长事务的核心危害是"阻塞版本回收"与"长时间持锁"。理解它为何拖慢 VACUUM/purge 是 MySQL 与 PostgreSQL 共同关注点。

#
★★★

6. 隐式事务与显式事务的差异,自动提交(AUTOCOMMIT)的影响?

请说明隐式事务与显式事务的差异,以及 AUTOCOMMIT 对事务边界的影响?

  • 隐式事务由单条语句自动包裹。
  • 显式事务用 BEGIN/COMMIT 明确控制。
  • AUTOCOMMIT 决定单条语句是否自动提交。

隐式事务指每条 SQL 语句自动作为一个事务(AUTOCOMMIT 开启时,单条语句执行后自动提交);显式事务用 BEGIN/START TRANSACTION 显式开启,用 COMMIT/ROLLBACK 结束,可包含多条语句构成一个原子单元。AUTOCOMMIT 参数控制:开启时(默认),每条语句自动提交,无需显式 COMMIT;关闭时,需显式 COMMIT 否则修改不生效(且会话结束时未提交事务回滚)。隐式事务适合单语句操作,显式事务适合多语句需原子性(转账、批量操作)的场景。若误用隐式事务做多语句操作,会破坏原子性。

隐式=单条自动提交,显式=BEGIN/COMMIT 组事务。AUTOCOMMIT 决定自动提交与否,是事务边界的基础。

#
★★★

7. 分布式事务的 ACID 弱化,CAP 理论下的取舍?

请说明分布式事务中 ACID 的弱化,以及 CAP 理论下的取舍?

  • 分布式环境难以保证强 ACID。
  • CAP 理论:一致性、可用性、分区容忍性只能取二。
  • 常采用最终一致性、BASE 等放松。

在分布式系统中,跨节点事务难以保证数据库级的强 ACID。CAP 理论指出:在分区(P)发生时,一致性(C)与可用性(A)不能同时保证。分布式事务需在 C 与 A 间取舍:选择强一致性(如 2PC)则牺牲可用性(协调者故障时不可用);选择可用性则需弱化一致性(最终一致性)。因此分布式事务常弱化 ACID:原子性靠 2PC/Saga 近似,隔离性放松(BASE 的"基本可用、软状态、最终一致"),一致性变为最终一致。关键取舍:强一致(2PC、XA)适合一致要求高但可接受阻塞的场景;最终一致(Saga、消息、TCC)适合高可用、可接受短暂不一致的场景。理解 ACID 在分布式下的弱化是分布式事务设计的核心。

分布式事务弱化 ACID 源于 CAP 的取舍。强一致牺牲可用性,最终一致牺牲即时一致性。这是分布式事务理论的核心。

#
★★★

8. WAL 如何支撑事务的持久性与崩溃恢复,刷盘时机与性能如何权衡?

请说明 WAL(Write-Ahead Logging)如何支撑事务持久性与崩溃恢复,以及刷盘时机与性能的权衡?

  • WAL 先写日志后写数据页(write-ahead)。
  • 崩溃时通过 WAL 重放恢复。
  • 刷盘时机(synchronous_commit、fsync)权衡持久性与性能。

WAL(Write-Ahead Logging)是事务持久性的核心:修改数据前,先把修改记录(redo log)写入 WAL 日志,再写数据页。事务提交时,只要 WAL 记录已落盘(fsync),即使数据页未写,崩溃后也能通过重放 WAL 恢复,保证持久性。刷盘时机决定持久性与性能的权衡:synchronous_commit=on(默认)时,事务提交需等待 WAL 刷盘(fsync),持久性最强但每提交一次有 fsync 延迟;synchronous_commit=off 时,提交不等待刷盘,性能高但崩溃可能丢失最近提交。fsync 频率很高时,group commit(组提交)把多个事务的 WAL 合并一次刷盘,减少 fsync 次数,提升吞吐同时保持持久性。MySQL 的 innodb_flush_log_at_trx_commit 类似:值为 1 时每次提交刷盘(持久性最强),0/2 时放松刷盘换取性能。

WAL 的核心是"先写日志、崩溃重放"。刷盘时机(synchronous_commit/innodb_flush_log_at_trx_commit)与 group commit 是持久性与性能的权衡点。

#
★★★

9. MySQL InnoDB 默认 REPEATABLE READ,PostgreSQL 默认 READ COMMITTED 的差异?

请说明 MySQL InnoDB 默认 REPEATABLE READ 与 PostgreSQL 默认 READ COMMITTED 的差异?

  • 两者默认隔离级别不同。
  • RR 全事务快照,RC 每语句快照。
  • 对不可重复读、幻读的影响。

MySQL InnoDB 默认隔离级别是 REPEATABLE READ(RR),PostgreSQL 默认是 READ COMMITTED(RC)。差异核心在快照时机:RR 下,事务第一次读时建立快照并复用,整个事务看到一致快照,可避免不可重复读与部分幻读(InnoDB 用 Next-Key Lock 防幻读);RC 下,每个语句建立新快照,看到的最新已提交数据,其他事务提交后,同一事务的后续语句可能看到不同数据(不可重复读)。因此 InnoDB 默认 RR 更严格,PostgreSQL 默认 RC 更宽松(性能更好,快照更简单)。两者 MVCC 实现不同:InnoDB 用 undo log + 版本链,PostgreSQL 用 xmin/xmax + 快照。选择默认值体现了不同的历史与权衡。

核心差异是"RR 全事务快照 vs RC 每语句快照"。这也解释了 InnoDB 用锁防幻读而 PostgreSQL RC 允许不可重复读。

#
★★★

10. MySQL 中设置隔离级别,SET TRANSACTION ISOLATION LEVEL?

请说明 MySQL 中设置隔离级别的方式,包括 session 级与事务级?

  • SET TRANSACTION ISOLATION LEVEL 语法。
  • SESSION/GLOBAL 作用域。
  • 事务级与全局级设置。

MySQL 设置隔离级别用 SET TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}。作用域可用 SET SESSION TRANSACTION ISOLATION LEVEL ...(仅当前会话)或 SET GLOBAL TRANSACTION ISOLATION LEVEL ...(全部新会话,需 SUPER 权限)。不带 SESSION/GLOBAL 时,是"事务级"设置,仅影响当前事务(需在事务开始前设置)。也可通过 SET @@transaction_isolation = '...' 设置。session 级影响当前会话后续所有事务,事务级仅影响当前事务。默认级别为 REPEATABLE READ。设置后需注意事务边界,事务级设置应在 BEGIN 之前。

隔离级别设置分 global/session/transaction 三级作用域。事务级需在 BEGIN 前设置,session 级影响当前会话。

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
#
★★★

11. PostgreSQL 中 READ UNCOMMITTED 等同于 READ COMMITTED(无脏读)?

请说明 PostgreSQL 中 READ UNCOMMITTED 为何等同于 READ COMMITTED,以及为何不发生脏读?

  • PostgreSQL 无脏读,READ UNCOMMITTED 被当作 READ COMMITTED。
  • MVCC 读已提交版本,未提交数据不可见。
  • 与 MySQL 的 READ UNCOMMITTED 行为差异。

PostgreSQL 中 READ UNCOMMITTED 与 READ COMMITTED 行为相同,都不会发生脏读。原因:PostgreSQL 基于 MVCC,读操作只读取已提交的版本,未提交事务的修改对任何读都不可见,因此无论隔离级别如何,都不会读到未提交数据(无脏读)。因此 READ UNCOMMITTED 被当作 READ COMMITTED 处理。这与 MySQL InnoDB 不同:MySQL 的 READ UNCOMMITTED 允许脏读(读到未提交数据)。PostgreSQL 的查询只读已提交版本,天然防脏读,是 MVCC 设计的优点。SERIALIZABLE 才需要额外冲突检测,READ UNCOMMITTED/COMMITTED/REPEATABLE READ 都基于快照。

PostgreSQL 的 MVCC 读已提交版本,天然无脏读,故 READ UNCOMMITTED 退化为 READ COMMITTED。与 MySQL 的 READ UNCOMMITTED 允许脏读形成对比。

#
★★★

12. REPEATABLE READ 在 PostgreSQL 中的快照读实现?

请说明 PostgreSQL 中 REPEATABLE READ 的快照读实现,以及它的特性?

  • RR 下事务开始建立快照并复用。
  • 全事务读取一致快照。
  • 避免不可重复读,但允许写偏斜。

PostgreSQL 的 REPEATABLE READ(RR)在事务第一次执行查询时建立快照(记录所有活跃事务的 xid 集合),并在此事务期间复用该快照。因此事务内的所有查询看到的都是一致的快照,即使其他事务提交了新数据,本事务也看不到(避免不可重复读)。RR 下读取基于快照,不要求额外锁。但 RR 不保证可串行化:两个事务可能基于同一快照做写决策,产生写偏斜(write skew),此时 RR 不检测。此外,RR 下 UPDATE 若发现目标行已被其他事务修改,会基于最新版本应用 EvalPlanQual 重检查,可能抛出"cannot serialize"错误。整体上 RR 提供全事务一致读,但允许写偏斜。

RR 的核心是"事务开始建快照并复用"。它避免不可重复读,但允许写偏斜(需 SERIALIZABLE 才检测)。

#
★★★

13. SERIALIZABLE 在 PostgreSQL 中基于 SSI(Serializable Snapshot Isolation)的实现?

请说明 PostgreSQL 的 SERIALIZABLE 基于 SSI(Serializable Snapshot Isolation)的实现原理?

  • SSI 基于快照隔离,检测序列化冲突。
  • 通过谓词锁与 SIReadLock 检测 rw-antidependency 环。
  • 冲突时回滚(40001)。

PostgreSQL 的 SERIALIZABLE 基于 SSI(Serializable Snapshot Isolation),它结合快照隔离(高并发、无锁读)与可串行化保证。实现原理:在快照隔离基础上,SSI 检测事务之间的读写依赖(rw-antidependency),即事务 T1 读某行、T2 写该行,且二者在时间上交错。通过为每个事务维护已读集合(用谓词锁 SIReadLock 标识读过的范围/行),并持续跟踪依赖关系,当检测到多个事务的 rw-antidependency 形成环(cycle)时,说明存在不可串行化的冲突,SSI 选择回滚其中一个事务(返回 SQLSTATE 40001 serialization failure)。应用层需检测 40001 并重试。相比传统 2PL 串行化,SSI 允许更多并发(无锁读),牺牲一定冲突检测开销。

SSI 的关键是"快照隔离 + 谓词锁 + 检测 rw-antidependency 环"。冲突时回滚一个事务,应用需重试 40001。

#
★★★

14. SQL Server 的 SNAPSHOT 隔离级别与 RCSI(Read Committed Snapshot Isolation)的差异?

请说明 SQL Server 的 SNAPSHOT 隔离级别与 RCSI(Read Committed Snapshot Isolation)的差异?

  • SNAPSHOT 是显式隔离级别,事务级快照。
  • RCSI 是 READ COMMITTED 的变体,语句级快照。
  • 两者都基于行版本,但语义不同。

SQL Server 提供了两种基于行版本(row versioning)的隔离机制。SNAPSHOT 隔离是一个显式隔离级别:事务开始时建立快照并在整个事务期间复用,提供可重复读 + 读不阻塞写(事务级快照),类似 PostgreSQL 的 RR。RCSI(Read Committed Snapshot Isolation)是 READ COMMITTED 的变体:每个语句建立新快照,语句级一致读,避免脏读,但事务内不同语句可能看到不同数据(允许不可重复读),类似 PostgreSQL 的 RC。两者差异核心在快照范围:SNAPSHOT 是事务级快照(全事务一致),RCSI 是语句级快照(每语句一致)。两者都避免了读阻塞写,但 SNAPSHOT 更严格(防不可重复读),RCSI 更宽松。使用需开启数据库 READ_COMMITTED_SNAPSHOT 或 ALLOW_SNAPSHOT_ISOLATION。

SNAPSHOT=事务级快照,RCSI=语句级快照。对应 PostgreSQL 的 RR 与 RC 的关系,都基于行版本避免读阻塞写。

#
★★★

15. SQL 标准定义的四个隔离级别,READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE 的语义?

请说明 SQL 标准定义的四个隔离级别(READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE)的语义?

  • 四个隔离级别从弱到强排列。
  • 各自允许的异常(脏读、不可重复读、幻读)。
  • 标准与数据库实现的差异。

SQL 标准定义四个隔离级别,从弱到强:READ UNCOMMITTED(允许脏读、不可重复读、幻读,最弱)、READ COMMITTED(禁止脏读,允许不可重复读与幻读)、REPEATABLE READ(禁止脏读与不可重复读,允许幻读)、SERIALIZABLE(全部禁止,最强)。隔离级别对应的异常:脏读读到未提交数据、不可重复读同一事务内相同查询结果不同、幻读同一事务内范围查询因其他事务插入而出现新行。注意标准与实现差异:MySQL InnoDB 的 RR 用 Next-Key Lock 防幻读(比标准更严格);PostgreSQL 的 READ UNCOMMITTED 当作 READ COMMITTED(无脏读)。SERIALIZABLE 在标准定义是"可串行化执行",实际实现用 2PL 或 SSI。

四个隔离级别逐级禁止脏读-不可重复读-幻读。需理解标准定义与各数据库实际实现的差异。

#
★★★

16. MySQL InnoDB 的默认隔离级别?

请说明 MySQL InnoDB 的默认隔离级别及其原因?

  • InnoDB 默认 REPEATABLE READ。
  • 通过 Next-Key Lock 实现防幻读。
  • 与标准 RR 的差异(更严格)。

MySQL InnoDB 的默认隔离级别是 REPEATABLE READ(RR)。相比标准 SQL 的 RR(允许幻读),InnoDB 的 RR 通过 Next-Key Lock(记录锁 + 间隙锁)在写入时锁定范围,防止幻读,因此 InnoDB 的 RR 实际比标准 RR 更严格,接近可串行化(在防幻读层面)。默认 RR 的原因:一是 MySQL 历史设计,保证主从复制与 binlog 在 RR 下的一致性;二是配合 Next-Key Lock 提供更强的读写一致性。虽然标准更高隔离级别是 SERIALIZABLE,但 InnoDB 默认 RR 已能防幻读,且相比 SERIALIZABLE 减少锁冲突。RC 下 InnoDB 基本禁用间隙锁,性能更好但允许幻读风险(对读无影响,对写有影响)。

InnoDB 默认 RR,靠 Next-Key Lock 防幻读,比标准 RR 更严格。这是 MySQL 与 PostgreSQL 默认隔离级别差异的背景。

#
★★★

17. PostgreSQL 的隔离级别行为?

请说明 PostgreSQL 各隔离级别的行为特点?

  • READ UNCOMMITTED 等同 READ COMMITTED。
  • RR 事务级快照,SERIALIZABLE 用 SSI。
  • 默认 READ COMMITTED。

PostgreSQL 的隔离级别行为:READ UNCOMMITTED 与 READ COMMITTED 完全相同(MVCC 无脏读)。READ COMMITTED(默认):每条语句建立新快照,看到最新已提交数据,允许不可重复读。REPEATABLE READ:事务第一次读时建立快照并复用,全事务一致读,避免不可重复读,但允许写偏斜。SERIALIZABLE:基于 SSI(Serializable Snapshot Isolation),检测读写依赖环,冲突时回滚一个事务(40001),应用需重试。PostgreSQL 默认 READ COMMITTED,相比 InnoDB 默认 RR 更宽松。所有级别都基于 MVCC 快照,读不阻塞写、写不阻塞读。SERIALIZABLE 是唯一需额外冲突检测的级别。

PostgreSQL 隔离级别基于 MVCC 快照,默认 RC,SERIALIZABLE 用 SSI。理解每级快照时机与冲突检测是核心。

#
★★★

18. 隔离级别与 MVCC 的关系,MVCC 如何实现读已提交与可重复读的快照隔离,幻读在 PostgreSQL 与 MySQL 中为何表现不同?

请说明隔离级别与 MVCC 的关系,MVCC 如何实现 RC 与 RR 的快照隔离,以及幻读在 PostgreSQL 与 MySQL 中为何表现不同?

  • MVCC 用版本 + 快照实现无锁读。
  • RC 每语句快照,RR 每事务快照。
  • 幻读差异:MySQL 用 Next-Key Lock,PostgreSQL 靠快照。

MVCC 是隔离级别实现的基础。它通过为每行保存多个版本(PostgreSQL 用 xmin/xmax,MySQL 用 undo log 版本链),结合快照(活跃事务集合)判定可见性,实现读写不阻塞。RC 下每个语句建立新快照,读到最新已提交;RR 下事务开始建立快照并复用,全事务一致。幻读表现差异:MySQL 的 RR 用 Next-Key Lock 在写入时锁范围,防止其他事务插入导致幻读(对写入的幻读防御);PostgreSQL 的 RR 纯靠快照(读时无锁),快照读本身不会"看到"新插入,但若事务内先读后写(INSERT/UPDATE 基于读判断),可能因数据变化而需 EvalPlanQual 重检查,产生"serialization"错误,而非静默幻读。因此幻读在 MySQL(锁防)与 PostgreSQL(快照 + 重检查)表现不同。

MVCC 提供快照隔离,RC/RR 决定快照范围。幻读差异源于 MySQL 用锁、PostgreSQL 用快照 + 写重检查。

#
★★★

19. SELECT FOR SHARE 的语义?

请说明 SELECT ... FOR SHARE 的语义,以及它与其他锁语句的关系?

  • FOR SHARE 加共享锁(S 锁)。
  • 多个事务可同时持有共享锁,但不能写。
  • 用于读后防修改。

SELECT ... FOR SHARE 对选中的行加共享锁(S 锁)。共享锁之间兼容:多个事务可同时持有同一行的共享锁,因此可并发读;但共享锁与排他锁(X 锁)互斥:任何事务不能再对该行加 X 锁(写会阻塞),直到共享锁释放。用途是"读后防修改":事务读取某行后,其他事务不能修改它,但可以继续读,适合需要读取一致性且不阻止并发读的场景。与 FOR UPDATE(加 X 锁,独占)相比,FOR SHARE 更宽松,允许并发读。在 PostgreSQL 与 MySQL 中 FOR SHARE 都有此语义。它用于实现读锁而避免写锁独占。

FOR SHARE 加 S 锁,允许并发读、阻止写。相比 FOR UPDATE 的 X 锁独占,FOR SHARE 更宽松。

#
★★★

20. MySQL InnoDB 中 SELECT(默认快照读)与 SELECT ... FOR UPDATE(当前读)的差异?

请说明 MySQL InnoDB 中普通 SELECT(快照读)与 SELECT ... FOR UPDATE(当前读)的差异?

  • 普通 SELECT 是快照读,读 MVCC 快照,不加锁。
  • SELECT ... FOR UPDATE 是当前读,读最新已提交,加 X 锁。
  • 差异影响一致性与并发。

在 MySQL InnoDB 中,普通 SELECT(无 FOR UPDATE)是快照读(snapshot read):基于 MVCC 快照读取行的历史版本,不加锁,不阻塞写,读写并发高。SELECT ... FOR UPDATE 是当前读(current read):读取最新已提交版本,并对读到的行加 X 锁(排他锁),直到事务结束,其他事务不能修改或加锁这些行。当前读用于需要"读到最新数据并锁定防并发修改"的场景(如 UPDATE 前置查询、乐观锁外的手动加锁)。差异核心:快照读不加锁、读旧版本;当前读加锁、读最新。快照读实现高并发,当前读保证读取最新 + 锁保护。合理选择两者平衡并发与一致性。

快照读(不加锁、读快照)vs 当前读(加锁、读最新)是 InnoDB 并发模型的核心。理解两者差异对设计并发控制至关重要。

#
★★★

21. PostgreSQL 中 SELECT 始终是快照读,UPDATE/DELETE 是当前读?

请说明 PostgreSQL 中 SELECT 是快照读、UPDATE/DELETE 是当前读的语义?

  • SELECT 读 MVCC 快照,不加锁。
  • UPDATE/DELETE 基于最新版本并加锁。
  • 写操作需处理并发修改。

PostgreSQL 中,普通 SELECT 始终是快照读:基于事务快照读取已提交版本,不加锁,不阻塞写。UPDATE/DELETE 是当前读/写操作:它们需要基于最新已提交版本执行修改,并对目标行加锁(行锁),防止并发修改。若 UPDATE/DELETE 的目标行在事务快照之后被其他事务修改并提交,PostgreSQL 会通过 EvalPlanQual 机制重检查最新版本,基于最新版本重新评估 WHERE 条件,若仍满足则更新该版本,否则跳过(或报错)。写操作之间的并发冲突由行锁解决。因此 PostgreSQL 的读(快照)与写(当前 + 锁)分离,实现读写不阻塞、写写互斥。

PostgreSQL 读是快照读、写是当前读加锁。写冲突由 EvalPlanQual 重检查解决,保护更新一致性。

#
★★★

22. 当前读的锁行为,FOR UPDATE 加 X 锁、FOR SHARE 加 S 锁?

请说明当前读(FOR UPDATE/FOR SHARE)的锁行为,X 锁与 S 锁的区别?

  • FOR UPDATE 加 X 锁(排他)。
  • FOR SHARE 加 S 锁(共享)。
  • 锁的兼容性矩阵。

当前读的锁行为:SELECT ... FOR UPDATE 对读取的行加 X 锁(排他锁),其他事务不能修改或加任何锁(S 或 X 都阻塞),直到事务结束;SELECT ... FOR SHARE 对读取的行加 S 锁(共享锁),其他事务可以加 S 锁(并发读)但不能加 X 锁(不能写)。锁兼容性:S 锁与 S 锁兼容(可并持有),S 锁与 X 锁互斥,X 锁与任何锁互斥。写入(UPDATE/DELETE/INSERT)需要 X 锁。因此 FOR UPDATE 用于独占写保护,FOR SHARE 用于共享读保护。理解 X/S 锁的兼容矩阵是分析并发与死锁的基础。

X 锁独占、S 锁共享。S 与 S 兼容,X 与任何锁互斥。这是当前读锁行为与并发控制的核心。

#
★★

23. 快照读与写偏斜(Write Skew)的关联?

请说明快照读与写偏斜(Write Skew)的关联,以及为何快照隔离不防写偏斜?

  • 写偏斜:两个事务基于同一快照做不同写,结果不可串行化。
  • 快照隔离读不锁,无冲突检测。
  • 需 SERIALIZABLE(SSI)检测。

写偏斜(write skew)指两个事务都基于同一快照读取数据,然后各自做不同的写操作,最终结果无法对应任何串行执行顺序。例如两个值班医生,事务 A 读"至少一人值班"后置自己请假,事务 B 读同一快照后也置自己请假,最终无人值班。快照隔离(RR)下,读不加锁、无冲突检测,两个事务的读写依赖(rw-antidependency)不构成传统锁冲突,因此快照隔离不防写偏斜。只有 SERIALIZABLE 级别(PostgreSQL 用 SSI 检测 rw-antidependency 环)才能检测并回滚其中一个事务。因此快照读提供了高并发,但牺牲了写偏斜防御,需 SERIALIZABLE 弥补。

写偏斜是快照隔离固有的缺陷,源于读不锁、无冲突检测。SSI 通过检测读写依赖环解决。这是区分 RR 与 SERIALIZABLE 的关键。

#
★★

24. 快照读的隔离级别依赖,REPEATABLE READ 始终读取事务开始时的快照?

请说明 REPEATABLE READ 下快照读是否始终读取事务开始时的快照?

  • RR 下快照在事务首次读时建立。
  • 快照建立的精确时机。
  • 与 READ COMMITTED 的对比。

在 REPEATABLE READ(RR)下,快照在事务第一次执行查询(读)时建立,并在整个事务期间复用,而不是在事务开始时(BEGIN 时)建立。因此后续所有查询都基于该快照,看到一致的数据。若事务在首次读前有写操作或无读,快照以首次读为准。这与 READ COMMITTED 不同:RC 每个语句建立新快照。PostgreSQL 的 RR 在事务中第一次查询时建立快照;MySQL InnoDB 的 RR 在第一个一致性读(快照读)时建立 Read View。理解"事务首次读时"而非"事务开始时"建快照,是 RR 实现的精确细节。快照在事务生命周期内固定,保证全事务一致读。

RR 快照在"事务首次读"时建立并复用,而非 BEGIN 时。这一精确时机是区分 RR 与 RC 的关键细节。

#
★★

25. RR 隔离级别下的快照读与 Phantom 问题?

请说明 RR 隔离级别下快照读与幻读(Phantom)问题,以及在 MySQL 与 PostgreSQL 中的表现?

  • 快照读本身不会看到幻行。
  • 幻读定义:范围查询结果因其他事务插入而变化。
  • MySQL 用 Next-Key Lock 防写幻读,PostgreSQL 靠快照。

幻读(phantom)指在同一事务中,范围查询的结果因其他事务插入(或删除)新行而出现变化。在 RR 的纯快照读下,事务内所有查询读同一快照,因此快照读本身"看不到"幻行——PostgreSQL 的 RR 快照读天然避免幻读(读一致性)。但 MySQL InnoDB 的 RR 需要考虑写入场景:普通快照读(SELECT)不产生幻读,但当前读(SELECT ... FOR UPDATE / UPDATE / DELETE)的加锁范围需防幻读,InnoDB 用 Next-Key Lock(记录锁+间隙锁)锁定范围,阻止其他事务在该范围插入,从而防止写入时的幻读。因此:PostgreSQL 靠快照读天然防读幻读;MySQL 靠 Next-Key Lock 防写幻读。两者机制不同。

快照读靠快照一致性防幻读,当前读靠锁(Next-Key Lock)防幻读。理解 PostgreSQL 与 MySQL 机制差异是关键。

#
★★

26. MySQL FOR UPDATE 的语义?

请说明 MySQL 中 SELECT ... FOR UPDATE 的语义与用途?

  • FOR UPDATE 加 X 锁,独占锁定。
  • 当前读,读最新已提交。
  • 用于悲观锁、防并发修改。

MySQL 的 SELECT ... FOR UPDATE 是当前读,读取最新已提交版本,并对读取的行加 X 锁(排他锁),直到事务提交或回滚才释放。其他事务不能修改这些行,也不能对其加 FOR UPDATE/FOR SHARE 锁(会阻塞等待)。用途是悲观锁:在事务中读锁数据,防止其他事务并发修改,用于需要串行化保护的场景(如余额扣减、库存扣减)。与普通 SELECT(快照读)不同,FOR UPDATE 保证读到最新数据并锁定。在 RR 下,FOR UPDATE 还会配合 Next-Key Lock 锁范围防幻读。需注意:加锁到事务结束,因此应尽量缩短事务持锁时间,避免长事务。

FOR UPDATE 是当前读 + X 锁,用于悲观锁。理解其读最新、加锁到事务结束的语义,以及锁范围与事务时长的权衡。

#
★★

27. SSI 的谓词锁(Predicate Lock)与 SIReadLock 的语义?

请说明 SSI 中谓词锁(Predicate Lock)与 SIReadLock 的语义及其作用?

  • 谓词锁记录读过的谓词范围。
  • SIReadLock 标记事务读过的行/范围。
  • 用于检测读写依赖。

在 SSI(Serializable Snapshot Isolation)中,为了检测读写依赖(rw-antidependency),需要记录每个事务读过的范围。谓词锁(predicate lock)是逻辑上的锁,实现"事务读过的谓词范围"的追踪——它不实际阻塞,而是记录信息。实际的谓词锁实现是 SIReadLock:当事务通过快照读某行或范围时,PostgreSQL 会为这些行/范围记录 SIReadLock(底层用索引或页级的 RW 锁实现),标记"该事务读了这个范围"。当另一个事务写入该范围时,SSI 检测到读写依赖,若构成环则回滚。即 SIReadLock 是 SSI 检测冲突的"读标记",谓词锁是理论模型,SIReadLock 是其实现。它们不阻塞读写,只用于冲突检测。

谓词锁是理论模型,SIReadLock 是实际实现,记录事务读过的范围用于检测读写依赖环。不阻塞读写,只检测。

#
★★

28. SSI(Serializable Snapshot Isolation)的核心思想,检测序列化异常(rw-antidependency cycles)?

请说明 SSI 的核心思想,包括如何检测 rw-antidependency cycles?

  • SSI 在快照隔离基础上检测序列化冲突。
  • rw-antidependency:读后写依赖。
  • 检测到环时回滚一个事务。

SSI(Serializable Snapshot Isolation)的核心思想是:在快照隔离(无锁读、高并发)的基础上,额外检测可能导致不可串行化的读写依赖(rw-antidependency),从而保证可串行化。rw-antidependency 指:事务 T1 读某行/范围,事务 T2 写该行/范围,且 T2 的写发生在 T1 之后(T1 的快照看不到 T2 的写)。SSI 通过 SIReadLock 记录读,持续跟踪这些依赖。当若干事务的 rw-antidependency 形成环(cycle)时,说明这组事务的并发执行无法对应任何串行顺序,SSI 选择回滚其中一个事务(返回 40001),保证剩余事务可串行化。因此 SSI 提供快照隔离的并发度 + 可串行化保证,代价是冲突检测开销与偶尔回滚。

SSI 核心是"快照隔离 + 检测 rw-antidependency 环"。环存在即回滚一个事务,保证可串行化。这是 PostgreSQL SERIALIZABLE 的实现。

#
★★

29. 写偏斜(Write Skew)的检测与回滚?

请说明写偏斜(Write Skew)的检测与回滚机制?

  • 写偏斜由快照隔离导致。
  • SSI 检测读写依赖环。
  • 回滚一个事务,应用重试。

写偏斜(write skew)是快照隔离下特有的不可串行化异常:两个事务基于同一快照读,各自写不同数据,结果无法串行化。检测写偏斜依赖 SSI:SSI 通过 SIReadLock 记录事务读过的行/范围,检测写与读之间的 rw-antidependency。当检测到多个事务的读写依赖构成环(cycle),即存在写偏斜时,SSI 选择回滚其中一个事务(返回 SQLSTATE 40001 serialization failure)。被回滚的事务需应用层捕获 40001 并重试。检测的粒度是事务级,可能回滚较新的或根据一定启发式选择。这样既允许快照隔离的高并发,又通过检测+回滚保证可串行化。

写偏斜检测靠 SSI 的读写依赖环检测,回滚一个事务。应用需重试 40001。这是可串行化与并发的平衡。

#
★★

30. 原子性(Atomicity)的实现,日志(Write-Ahead Log)保证部分提交回滚?

请说明原子性(Atomicity)的实现,特别是日志如何保证部分提交回滚?

  • 原子性要求全成功或全回滚。
  • undo log 记录旧版本,用于回滚。
  • WAL 用于崩溃恢复。

原子性(Atomicity)保证事务要么全部提交要么全部回滚。实现核心是日志:修改前先记录 undo 信息(旧值),事务回滚时用 undo 恢复旧值;若崩溃,通过 undo 撤销未提交事务的修改。PostgreSQL 用 WAL 记录事务;MySQL InnoDB 用 undo log(记录行旧版本,通过 DB_ROLL_PTR 指向)实现回滚。事务提交时,只有所有操作都成功才标记提交;任一操作失败则整体回滚,部分提交的修改被撤销。崩溃恢复时,redo 重放已提交事务,undo 回滚未提交事务,保证原子性。因此"日志(undo/WAL)保证部分提交回滚"是原子性的实现机制。

原子性靠 undo 日志回滚未完成事务。Undo 记录旧版本,回滚时恢复。崩溃恢复用 undo 撤销未提交修改。

#
★★

31. 持久性(Durability)的实现,fsync、组提交(Group Commit)?

请说明持久性(Durability)的实现,包括 fsync 与组提交(Group Commit)?

  • 持久性要求提交后数据不丢失。
  • fsync 将日志刷盘。
  • group commit 合并多次刷盘。

持久性(Durability)保证事务提交后,即使崩溃也不丢失修改。实现的本质是提交时把 WAL/redo 日志落盘(fsync),因为数据页可能仍在内存,但日志已落盘即可在崩溃时重放。fsync 是同步刷盘系统调用,确保日志真正写入磁盘。为提升吞吐,组提交(group commit)把多个事务的日志合并为一次 fsync,减少刷盘次数,同时保持持久性(每个事务的日志都包含在本次刷盘中)。PostgreSQL 的 synchronous_commit=on 时提交等待 fsync,同时支持 group commit 合并;MySQL 的 innodb_flush_log_at_trx_commit=1 时每次提交刷盘,并支持 group commit 批量刷盘。若关掉 fsync(off),性能高但崩溃可能丢数据,持久性降低。因此持久性 = fsync 落盘 + group commit 优化。

持久性靠提交时 fsync 日志落盘,group commit 合并刷盘提升性能。这是持久性与吞吐的权衡实现。

#
★★

32. 三种读取异常(Dirty Read、Non-Repeatable Read、Phantom Read)的定义?

请说明脏读(Dirty Read)、不可重复读(Non-Repeatable Read)、幻读(Phantom Read)的定义?

  • 脏读:读到未提交数据。
  • 不可重复读:同一查询结果不同。
  • 幻读:范围查询出现新行。

三种读取异常的定义:脏读(Dirty Read):一个事务读到另一个事务未提交的数据,若后者回滚,则读到的是"脏"数据。不可重复读(Non-Repeatable Read):同一事务内,两次相同查询因其他事务提交而返回不同结果(某行被修改)。幻读(Phantom Read):同一事务内,两次范围查询因其他事务插入新行而返回不同的行集合(出现"幻影"行)。隔离级别逐级防御:READ UNCOMMITTED 允许脏读;READ COMMITTED 禁止脏读但允许不可重复读与幻读;REPEATABLE READ 禁止脏读与不可重复读但允许幻读;SERIALIZABLE 全部禁止。理解三种异常是隔离级别的基础。

三种异常按"读到未提交/读到更新/读到新行"区分,隔离级别逐级防御。这是事务隔离理论的核心概念。

#
★★

33. REPEATABLE READ 的应用?

请说明 REPEATABLE READ(RR)的应用场景?

  • RR 提供全事务一致读。
  • 适合需要事务内一致快照的场景。
  • 用于报表、对账、长事务读。

REPEATABLE READ(RR)提供事务内一致快照,适合需要"整个事务看到一致数据"的场景。典型应用:报表生成(事务内多次查询需一致,避免中途数据变化导致报表不一致)、对账(多表/多次查询需一致基线)、需要可重复读的业务逻辑(事务内多次读取同一数据应一致)。RR 保证不可重复读被避免,适合读多写少、对一致性要求高的场景。InnoDB 默认 RR 且用 Next-Key Lock 防幻读,适合需要防幻读的写场景。相比 READ COMMITTED,RR 有更高的一致性保证,但快照维护成本略高(快照复用)。选择 RR 时需权衡一致性需求与并发/性能。

RR 适合事务内一致读场景(报表、对账、可重复读业务)。理解其快照复用与防不可重复读特性是应用选择的关键。

#
★★

34. 快照读(Snapshot Read)与当前读(Current Read)的语义差异,读快照 vs 读最新已提交?

请说明快照读(Snapshot Read)与当前读(Current Read)的语义差异?

  • 快照读读 MVCC 快照,不加锁。
  • 当前读读最新已提交,加锁。
  • 适用场景差异。

快照读(snapshot read)与当前读(current read)是 InnoDB 的两种读方式。快照读:基于 MVCC 快照读取行的历史版本,不加锁,不阻塞写,并发高,但读到的是快照时刻的数据(可能不是最新)。当前读:读取最新已提交版本,并对读取的行加锁(FOR UPDATE 加 X 锁、FOR SHARE 加 S 锁,或 UPDATE/DELETE/INSERT 触发的当前读),保证读到最新并锁保护,但并发低(可能阻塞)。普通 SELECT 是快照读;SELECT ... FOR UPDATE、FOR SHARE、UPDATE、DELETE、INSERT 是当前读。语义差异:快照读"读快照、不加锁",当前读"读最新、加锁"。选择取决于是否需要最新数据与锁保护。

快照读不加锁读快照,当前读加锁读最新。这是 InnoDB 并发模型的核心,理解差异对设计一致性方案至关重要。

#
★★

35. SERIALIZABLE 与 REPEATABLE READ 的根本差异,除快照读外的额外冲突检测?

请说明 SERIALIZABLE 与 REPEATABLE READ 的根本差异,特别是额外冲突检测?

  • RR 只提供快照一致读。
  • SERIALIZABLE 额外检测写冲突(写偏斜)。
  • PostgreSQL 用 SSI,MySQL 用锁。

REPEATABLE READ(RR)与 SERIALIZABLE 的根本差异在于:RR 只提供快照一致读(避免不可重复读),但不检测写偏斜等不可串行化异常;SERIALIZABLE 在快照读之外,额外检测并发冲突,确保结果等价于串行执行。PostgreSQL 的 SERIALIZABLE 用 SSI 检测 rw-antidependency 环,冲突时回滚一个事务(40001);MySQL 的 SERIALIZABLE 用更强的锁(所有读都变当前读加锁,或用 Next-Key Lock)阻止并发写。因此 RR 允许写偏斜,SERIALIZABLE 检测并阻止。这是两者核心差异:RR 是"快照隔离",SERIALIZABLE 是"可串行化保证"。应用选择 SERIALIZABLE 需接受额外冲突检测开销与回滚。

RR 不防写偏斜,SERIALIZABLE 额外检测写冲突。差异核心是"是否检测不可串行化异常"。PostgreSQL 用 SSI,MySQL 用锁。

#
★★

36. SSI 的失败检测,40001 序列化失败时的应用重试?

请说明 SSI 检测到序列化失败(40001)时,应用应如何重试?

  • SSI 冲突时回滚返回 40001。
  • 应用需捕获 40001 并重试。
  • 重试需幂等与退避。

当 SSI 检测到序列化冲突(rw-antidependency 环)时,会回滚其中一个事务并返回 SQLSTATE 40001(serialization failure)。应用无法在事务内解决该冲突,必须捕获错误、回滚整个事务,然后重新开启事务重试。重试注意事项:一是重试前需保证事务是幂等的(重复执行不产生重复副作用),否则可能重复写入;二是重试应加退避(backoff)避免高并发下反复冲突;三是重试次数有限(避免无限重试);四是重试时为整个事务重试,而非局部语句。在 SERIALIZABLE 隔离级别下,应用必须处理 40001 重试,这是使用可串行化隔离的固有要求。

40001 需应用捕获、回滚、重试整个事务。重试需幂等与退避。这是 SERIALIZABLE 隔离的应用层要求。

#
★★

37. fsync 频率如何影响事务持久性与吞吐,group commit 的作用是什么?

请说明 fsync 频率如何影响事务持久性与吞吐,以及 group commit 的作用?

  • fsync 频率决定持久性强度。
  • 高频 fsync 吞吐低、持久性强。
  • group commit 合并刷盘提升吞吐。

fsync 频率直接影响事务持久性与吞吐的权衡。fsync 频率高(每次提交都刷盘)时,持久性最强(崩溃不丢已提交数据),但每次提交有 fsync 磁盘 I/O 延迟,吞吐低。fsync 频率低(不刷盘或延迟刷盘)时,吞吐高,但崩溃可能丢失最近提交的数据,持久性弱。group commit(组提交)的作用是:把多个事务的日志合并为一次 fsync,既保证每个事务的日志都落盘(持久性),又减少 fsync 次数(提升吞吐),是持久性与吞吐的优化平衡。PostgreSQL 的 synchronous_commit 与 MySQL 的 innodb_flush_log_at_trx_commit 控制刷盘频率,group commit 自动合并。理解 fsync 频率与 group commit 是持久性调优的核心。

fsync 频率决定持久性,group commit 合并刷盘提升吞吐。这是持久性与性能的核心权衡。

#
★★

38. READ COMMITTED 的应用?

请说明 READ COMMITTED(RC)的应用场景?

  • RC 每语句读最新已提交。
  • 适合读最新数据、并发高的场景。
  • 允许不可重复读。

READ COMMITTED(RC)提供每语句最新已提交读,适合需要"读到最新已提交数据"且并发高的场景。典型应用:OLTP 在线交易(读最新订单状态、余额)、实时查询(及时看到其他事务提交的更新)、需要避免脏读但可接受不可重复读的场景。RC 避免了脏读(读已提交),保证每条语句读到最新,但允许同一事务内不可重复读(其他事务提交后,后续语句看到新数据)。PostgreSQL 默认 RC,适合大多数 OLTP 场景。相比 RR,RC 快照更简单、开销更低,且无写偏斜风险(只读场景)。适合读多写少、数据频繁更新的实时系统。选择 RC 需接受不可重复读。

RC 适合读最新已提交、高并发的 OLTP 场景。允许不可重复读但避免脏读,是大部分在线业务默认选择。

#

39. SSI 的性能开销,谓词锁维护代价?

请说明 SSI 的性能开销,特别是谓词锁维护代价?

  • SSI 记录读范围(SIReadLock)。
  • 谓词锁维护有内存与 CPU 开销。
  • 影响并发与吞吐。

SSI(Serializable Snapshot Isolation)相比快照隔离有额外性能开销。主要开销是谓词锁(SIReadLock)的维护:为每个事务读过的行/范围记录 SIReadLock,需要额外内存存储读标记,并跟踪读写依赖关系(rw-antidependency),这需要 CPU 与内存开销。冲突检测需持续评估依赖是否成环,增加执行开销。因此 SSI 在并发读写频繁、依赖复杂的场景下开销更大,可能降低吞吐。此外,SIReadLock 的粒度(行级或页级)影响精度与开销:粒度越细越准确但开销越大。优化器/数据库在 SERIALIZABLE 模式下才启用 SSI。使用 SERIALIZABLE 需权衡一致性与性能开销,适合对一致性要求高且并发冲突少的场景。

SSI 开销来自谓词锁记录与依赖追踪。粒度、内存、CPU 是主要代价。SERIALIZABLE 需权衡一致性收益与性能开销。

#

40. SQLSTATE 40001 的含义?

请说明 SQLSTATE 40001 的含义?

  • 40001 表示 serialization failure(序列化失败)。
  • 常见于 SERIALIZABLE 隔离。
  • 应用需重试。

SQLSTATE 40001 表示 serialization failure(序列化失败)。它发生在数据库无法保证串行化执行的场景,常见于 SERIALIZABLE 隔离级别下,数据库检测到事务间的冲突(如 SSI 检测到读写依赖环、锁冲突导致无法串行化)而回滚当前事务。PostgreSQL 的 SSI 在冲突时返回 40001。应用收到 40001 后,应回滚事务并重试整个事务(而非局部语句)。它通常在并发事务冲突时出现,是使用高隔离级别(SERIALIZABLE)必须处理的错误。记录中 40001 也代表事务被选为牺牲者回滚。应用层正确处理 40001 重试是保证可串行化隔离下数据正确性的关键。

40001 是序列化失败错误码,SSI 冲突时回滚返回。应用需捕获并重试整个事务。这是 SERIALIZABLE 隔离下的关键错误处理。