想象你经营着一间巨大的公共图书馆。书库里有几万个书架,读者随时进来借阅,图书管理员负责把新书放上书架、把旧书替换掉。一开始图书馆管理很粗放:谁想改书,就直接把书架上的那本抽出来涂改,改完放回去。于是问题来了——一个读者正捧着某本书在读,另一个管理员却把这本书改了;两个管理员同时想改同一本书,后改的人把先改的人覆盖掉了;甚至有人改到一半突然停电,书架上留下一本缺了十几页的残书。 这就是数据湖最初的样子:便宜、能装海量数据的对象存储,像极了那个管理混乱的大书库。文件只能整体覆盖、没有并发保护、改到一半崩溃就会留下半成品。后来,Iceberg、Delta Lake 和 Hudi 这三个「图书管理公司」出现了。它们不换书架、不动书库,而是在书架旁加了一套严格的馆员制度——ACID 事务支持。从此,读者看到的是固定版本的馆藏,写者改书不会互相踩踏,改到一半断电也能安全回滚。这本书,就讲这套制度是怎么让数据湖变得可信的。
掌握数据湖 ACID 机制,是成为大数据架构师和云数据平台工程师的核心加分项——它是数据湖从「能存」走向「靠谱」的关键分水岭。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
事务的由来
回到那间大书库。为什么书库一开始乱?因为书架本身是「只会放书、不会改书」的:要更新一本书,只能把整本书从架上抽走,放一本全新的上去。如果两个管理员同时操作,后放的会把先放的盖掉;如果抽到一半停电,书架上留下的是残书。对象存储(Object Storage)正是这样的书架——它只支持整体写入和覆盖,不支持原地修改、也不提供并发保护。数据湖把数据放在对象存储上,等于把成千上万本书直接摞在开放式书架上,却没有馆员管理。
后来人们发现,数据仓库早就解决了这个问题——它像一个有严格馆员制度的封闭阅览室,但容量有限、价格昂贵。数据湖想要的是「仓库的规矩 + 书库的容量」。于是事务层出现了:它不改变书架本身,而是在书架旁增加一套管理逻辑,让数据湖拥有数据库才有的 ACID 保证——原子性、一致性、隔离性、持久性。这三套方案就是 Iceberg、Delta Lake 和 Hudi。
在没有任何保护的书库里,读者和管理员频繁交互,更新丢失的风险与读写操作的数量密切相关。我们可以用一个简化的模型表达这种风险:
没有馆员的书库,风险与操作频率成正比。ACID 事务层就是那套「馆员制度」——它不换书架,却让整个书库有了秩序。理解了为什么需要它,接下来看它最核心的手段:让读者先「冻结」眼前的书架。
- $R$(读操作次数)在一段时间内,读者翻阅、查询数据的次数。这个值越大,意味着越多的「眼睛」正盯着可能被改动的数据。
- $W$(写操作次数)同一段时间内,写者修改、覆盖数据的次数。写操作是破坏风险的直接来源。
- $T$(时间窗口)统计这些操作的时间跨度,把风险归一化到单位时间,便于横向比较。
- $P_{\mathrm{lost}}$(更新丢失概率)读与写在同一窗口内互相干扰的可能性,代表数据不一致的隐患程度。
快照隔离
读者走进书库,管理员递给他一张印着编号的「快照卡」,上面写着:v1。接下来的半小时里,无论书架上发生什么变化——有人换书、有人加书、有人删书——这位读者看到的始终是 v1 编号对应的馆藏状态。其他读者拿到的是 v2、v3 的卡片,各看各的,互不干扰。这就是快照隔离(Snapshot Isolation)的直观感受:每个读者拥有一个「冻结时刻」的数据视图。
实现起来并不玄妙。事务层维护一份「版本指针」,指向当前有效的数据文件清单。每次提交事务,就生成一个新的清单并切换指针,旧的清单不删除。读者开始查询时,事务层把当时的指针「钉」在它的会话上,之后所有读取都沿着这个指针找文件。因为文件是不可变的、写后不覆盖,所以读者读到的内容天然稳定,甚至不需要加任何读锁。
数据湖的元数据层记录了所有版本间的先后关系。一个快照要么等于当前最新版本,要么早于它,绝不可能「晚于」最新版本——否则就出现了穿越时空的幻觉。
快照隔离让读者彻底安心:书架再怎么变,我手里的卡片决定我看到的世界。那么写者呢?他们怎么在不破坏旧书的前提下完成修改?答案是:别涂改原书,抄一本新的。
- $S_{\mathrm{latest}}$(最新版本号)当前书库里最新一次事务提交后的版本编号,代表最新的馆藏状态。
- $S_{\mathrm{snapshot}}$(快照版本号)读者进入阅览室那一刻被分配的版本编号,之后它的读取固定指向这个编号。
- $\preceq$(偏序关系)读作「不晚于」,表示版本之间存在先后顺序,且不要求任意两个版本都可比较。
- 整体含义读者手里的快照版本,要么是当前最新版,要么是历史某个旧版本,绝不可能超前。这保证快照视图是合理且自洽的。
Copy-on-Write
你是一个图书管理员,接到任务把《数据湖入门》第 3 章更新一下。按照老办法,你得从书架上抽出那本书,直接涂改——但这会破坏正捧着 v1 快照卡阅读的读者。事务层给出的规则简单粗暴:不许涂改原书,抄一本新的再改。你复制一本《数据湖入门》,在副本上完成第 3 章的修订,然后把它放上书架,签上新的版本号 v2。
这就是 Copy-on-Write(写时复制,简称 COW)机制。每次更新数据时,事务层把需要修改的数据文件复制一份,在副本上写入变更,提交时用新文件替换元数据清单中的旧条目。旧文件原封不动地留在存储里,仍然被 v1 快照的清单引用。读取路径因此变得极其简单:按清单指针找文件,拿到的永远是完整、一致的数据文件,不需要现场拼接。
代价是「写放大」——明明只改了一点点数据,却要把整个文件复制一遍。我们把写入放大系数定义为:
Copy-on-Write 让「改书」和「读书」彻底解耦:读者安心读旧版,写者放心写副本,互不侵犯。但问题来了——两个管理员同时抄同一本书怎么办?这就轮到乐观并发控制登场。
- $N$(新增数据字节)这笔事务真正想写入的新数据量,比如只更新了 1 个字段对应的 100 字节。
- $R$(重写的旧数据字节)因为复制旧文件而额外写入存储的数据量。文件越大,R 越惊人。
- $\mathrm{WA}$(写放大系数)实际写入存储的总字节与新增有效字节的比值,反映存储层承受的额外负担。
- 整体关系分子是总写入量,分母是有用写入量。系数等于 1 代表零浪费,系数越大代表复制开销越高。
乐观并发控制
两个管理员同时看上了《数据湖入门》,都想在第 3 章加一段新内容。悲观的做法是:给这本书上把锁,谁先拿到谁改,另一个排队等。数据湖的选择恰恰相反——乐观:你们各抄各的,尽管改。等要提交的时候,馆员问一句:「你开始抄的时候,原书是第几版?」如果两个人的答案都是 v1,而对方已经在 v1 的副本上提交了 v2,那后提交的人就会被告知:你已经基于过期版本修改了,请重新基于 v2 再来一遍。
乐观并发控制(Optimistic Concurrency Control)的核心思想是:写入时不加锁,提交时校验版本。事务引擎为每次事务记录它读取的基础版本号,提交时检查这个版本号是否仍然等于当前的元数据版本。提交成功的判定条件可以写作:
乐观并发控制特别适合数据湖的典型场景:读多写少。大多数时候提交都能一次通过,省去了持锁的开销。只有当两个写者真的撞车时,才需要有人退让一步。而在实现层面,Iceberg、Delta Lake 和 Hudi 正是把这套乐观逻辑落地成产品的三家公司。
- $V_{\mathrm{base}}$(基础版本号)事务开始执行时,写者基于哪个版本的数据进行了计算。这个版本号被记录在事务上下文中。
- $V_{\mathrm{commit}}$(提交版本号)事务准备提交时,从元数据层读到的当前最新版本号。
- 等号判定两者相等,说明准备期间没人抢先提交,本次修改安全,允许落盘;不等则证明有并发冲突。
- 冲突处理校验失败后,事务自动放弃本次写入,基于最新版本重新读取数据、重新计算、再次提交,直到成功或达到重试上限。
三大系统实现
现在有三家图书管理公司来竞标你的书库改造项目,它们分别是 Iceberg、Delta Lake 和 Hudi。三家公司都承诺提供 ACID 事务,但管理风格各有侧重。Iceberg 是一个「表格式」(Table Format)标准,它最激进地拥抱开放生态:不绑定任何计算引擎,Spark、Flink、Trino 都能用它管理数据,像一位中立的技术顾问,谁家引擎都能配合。它的元数据采用分层树状结构,快照管理非常高效。
Delta Lake 则深度绑定 Spark 生态,由 Databricks 公司主导。它的核心是一份可追加的事务日志(Transaction Log):每次提交就往日志里追加一条 JSON 记录,所有表状态都可以从这份日志里重建,像图书馆里那本永远只增不删的「馆务台账」。对于已经在用 Spark 的团队,Delta 的上手成本最低。Hudi 则把重心放在增量处理上,它最早为 Uber 的轨迹数据而生,擅长流式摄取和 Upsert(有则更新、无则插入),像一位专攻「物流入库」的运营专家。
三家的取舍,本质上是在读写成本之间做权衡。用一个简化模型来表达选型哲学:
三大公司各有所长,但它们的共同点是都保留了旧版本的数据文件。这正是我们接下来要介绍的隐藏宝藏——时间旅行。
- $C_{\mathrm{write}}$(写入成本)每次事务写入在存储和计算上的开销,包括复制文件、写日志、合并小文件等。
- $C_{\mathrm{read}}$(读取成本)查询数据时扫描、合并、解析文件的开销,直接决定报表和机器学习任务的响应速度。
- $\alpha$、$\beta$(权重系数)表达业务对读写性能的偏好。流式摄入场景 $\alpha$ 大,交互式分析场景 $\beta$ 大。
- $C_{\mathrm{total}}$(总成本)读写成本加权求和,选型时应取最小化这个值的方案。
时间旅行
书库保留了每一版书籍的完整副本,这听起来很浪费,但带来了一个神奇的能力:时间旅行。读者可以拿着 v1 的快照卡,要求查阅三个星期前的馆藏状态;管理员出了错,可以把整个书库回滚到昨天下午的版本;审计人员想知道《数据湖入门》一年来改过多少次,每次改了什么,翻快照记录就知道。数据湖的三大系统都支持这种能力——本质上是因为旧文件没有被删除,旧清单仍然指向它们。
时间旅行的技术实现,靠的是「清单文件」(Manifest)。每个快照版本都对应一组清单,清单里列出该版本包含的所有数据文件。查询某个历史版本时,引擎不再解析最新清单,而是解析该版本对应的旧清单,然后沿着清单扫描数据文件:
时间旅行让数据湖有了「记忆」,但也提醒我们:快照不会永远保留,存储成本会迫使管理员定期清理过期版本。最后,我们把目光投向流水线——当数据不是一批批到达,而是一条条持续涌来时,事务层如何应对?
- $v$(目标版本号)你想回到哪个历史时刻,比如 “2024-03-15 的快照”。
- $\mathrm{Manifest}_v$(版本清单)记录版本 v 包含哪些数据文件的元数据列表,是快照的「藏书目录」。
- $\mathrm{Scan}$(扫描操作)按清单逐个读取文件并执行查询计算的过程。
- $Q(v)$(版本查询)整个时间旅行的查询结果——只要清单未被清理,任何历史版本都可被重建。
写入模型对比
真实的数据世界里,数据不是一天运一车来的,而是像水管里的水一样持续流动:点击流、订单流、物联网日志,每秒都有新记录涌入。图书馆的比喻要升级了——这不再是管理员定期批量上书,而是一条加印流水线:不断有新版小册子从印刷机下来,需要立刻上架。这时,Copy-on-Write 的「复制一整本再改」就显得笨重:每个新事件都要复制文件,写放大和延迟都难以接受。
于是出现了 Merge-on-Read(读时合并) 模式:新到的数据先写成小体积的增量文件(Delta 文件),不打扰已有的基础文件;查询时再把基础文件和增量文件实时合并起来。代价是读取变慢——查询引擎要现场做「拼书」工作。Hudi 对这种模式支持得最成熟,非常适合高吞吐、频繁小批量写入的场景。而 Copy-on-Write 则仍然占据「读多写少」的主场。
无论哪种模式,事务层都让数据湖拥有了真正的 Upsert 语义:按主键判断,有则更新、无则插入,这直接支撑了流式更新——数据以分钟级延迟进入湖中,查询端看到的数据新鲜度可以用下面的差值来刻画:
从批量写入到流式更新,事务层让数据湖真正「动」了起来。回看整趟旅程:快照隔离给了读者固定视角,Copy-on-Write 给了写者安全操作的空间,乐观并发控制了结冲突,三大系统落地实现,时间旅行创造历史,写入模型对比指引选型——ACID 事务支持,就是让数据湖从「能存」到「靠谱」的那套馆员制度。
---
- $t_{\mathrm{ingest}}$(数据落盘时刻)最新一笔事务提交到事务层的时间,数据在此刻成为可查询的版本。
- $t_{\mathrm{now}}$(当前时刻)查询发起的时间。
- $R_{\mathrm{freshness}}$(新鲜度)两者之差,衡量湖中数据的最新程度。差值越小,说明从事件发生到可查询的延迟越低。
AI 应用场景
实时用户画像的增量更新
阶段:特征工程与特征存储
解决的问题:传统做法是把用户行为日志批量写入数仓,T+1 更新画像,特征滞后一天;而直接把流数据写入对象存储,又会出现并发覆盖导致画像错乱。事务层让数据湖支持 Upsert 与流式更新,行为事件以分钟级延迟合并进画像表,且每次合并都是一个原子事务。
具体案例:某推荐团队用 Hudi 存储用户实时特征,用户点击流经 Flink 清洗后每 5 分钟一次 Upsert 到湖中的画像表。查询引擎读取时通过 Merge-on-Read 合并增量文件,特征新鲜度从 24 小时降到 5 分钟,推荐点击率因此提升了 8%。
金融数据的审计与回滚
阶段:数据治理与模型合规
解决的问题:金融风控模型的训练数据需要可追溯、可回滚。如果上游修正了某天的数据,模型团队必须知道修正前后分别是什么样。传统数据仓库做不到低成本保留全量历史,对象存储的无版本覆盖又无法审计。时间旅行能力让分析师可以查询任意快照,并对误操作一键回滚。
具体案例:某支付公司的风控团队使用 Iceberg 存储交易事件表。一次上游脚本误更新了字段,分析师通过 TIMESTAMP AS OF 语法定位到出错前的快照,将表回滚到该版本,并用两份快照的差异定位了问题脚本,审计报告完整记录了全部操作链。
多团队共享的数据湖
阶段:数据中台与多团队协作
解决的问题:算法团队、报表团队、数仓团队共用同一个数据湖,各自向同一张表写入特征、清洗结果和汇总指标。没有事务控制时,后写的团队会覆盖前者的成果。乐观并发控制让各团队基于各自快照写入,提交时自动检测冲突,失败方基于最新版本重试,互不阻塞。
具体案例:某零售企业的湖上宽表由三个团队共同维护。采用 Delta Lake 事务日志后,并发写入冲突次数从每周十几次降到接近零,失败任务通过自动重试完成,数据团队之间的「互相踩踏」投诉消失了。
Python 演示
# -*- coding: utf-8 -*-"""极简快照存储:模拟数据湖 ACID 的 Copy-on-Write 核心语义.""" class SnapshotLibrary: """带快照隔离的简易键值存储. 写入不覆盖旧数据, 而是生成新版本, 读者可基于任意快照读取. 对应数据湖事务层: 多版本数据文件 + 清单指针切换. """ def __init__(self): self.versions = {} # version -> state dict self.current = 0 self.versions[0] = {} def snapshot(self): """返回当前版本号, 相当于读者领取快照卡.""" return self.current def write(self, key, value): """Copy-on-Write 写入: 复制当前状态, 修改副本, 提交新版本.""" new_state = dict(self.versions[self.current]) # 复制旧文件 new_state[key] = value # 在副本上修改 self.current += 1 # 新版本号 self.versions[self.current] = new_state # 提交快照 return self.current def read(self, key, version=None): """读取指定版本的数据, 默认为最新版本.""" v = self.current if version is None else version if v not in self.versions: raise KeyError("snapshot version not found") return self.versions[v].get(key) def time_travel(self, version): """时间旅行: 返回某个历史版本的完整视图.""" return dict(self.versions.get(version, {})) if __name__ == "__main__": lib = SnapshotLibrary() lib.write("user_001", {"name": "Alice", "age": 30}) v1 = lib.snapshot() lib.write("user_001", {"name": "Alice", "age": 31}) v2 = lib.snapshot() print("v1 快照看到的数据:", lib.read("user_001", v1)) print("v2 快照看到的数据:", lib.read("user_001", v2)) print("历史全量视图 v1:", lib.time_travel(1)) # 模拟乐观并发: 两个写者基于同一版本 base = v1 commit = lib.current if commit == base: print("乐观并发校验通过: 允许提交") else: print("乐观并发冲突: 需基于最新版本 v%d 重试" % commit)
new_state = dict(self.versions[self.current])这行执行 Copy-on-Write 的「复制旧文件」:完整拷贝当前版本的数据文件,准备在副本上修改。self.current += 1每笔写入分配一个新版本号,等价于事务提交后元数据层切换版本指针。self.versions[self.current] = new_state把副本作为新快照挂到版本号上,旧版本原样保留,快照隔离由此成立。def time_travel(self, version)直接返回任意历史版本的状态字典,这就是数据湖时间旅行能力的微型版。lib.read("user_001", v1)读取时必须显式传入版本号,演示「读者固定在某个快照视角」的语义。if commit == base:乐观并发控制的核心判定:提交版本与基础版本一致才允许落盘,否则自动重试。
常见误区
误区我以为数据湖装上 Iceberg/Delta 之后,就跟 MySQL 一样支持行级更新、行级锁和复杂事务了,可以完全替代数据库。
正解数据湖事务层的事务粒度是「文件 + 快照」层面,不是数据库的行级事务。它保证的是「一批文件原子替换」「读者看固定快照」「写者提交不互相覆盖」,而不是让对象存储变成关系型数据库。你可以把事务层理解为给书库定了「整本换书」的规矩,而不是给每行文字上了锁。需要高频行级更新时,仍应使用数据库或 KV 引擎。
为何常见:因为 ACID 这个词太熟悉了,人们自然把数据库的所有能力都投射到数据湖上,忽略了事务粒度和使用场景的差异。
误区COW 意味着每次修改都要把整个数据文件复制一份,存储成本翻倍增长,数据湖很快就会爆掉。
正解COW 只复制「被修改的那个文件」,未涉及的文件在新旧快照之间是共享的——前文的动画里看到,v2 快照并没有复制 B,而是继续引用 v1 的 B。共享未改文件是对象存储的天然优势,因为文件不可变且按引用来组织。实际写入放大系数取决于文件大小和更新频率,系统还会通过「小文件合并」来抑制膨胀。
为何常见:只看「复制」二字,忽略了共享引用机制,把逻辑多版本的存储开销当成了物理全量复制。
误区既然快照都在,那我可以让数据湖保留所有历史版本,随时回到任意时刻,永不清除。
正解每个快照都引用一批文件,旧快照越多,占用的存储越大。生产环境通常会配置快照保留周期(如保留最近 7 天或最近 100 个快照),过期快照由后台清理任务删除。「时间旅行」只对未被清理的快照有效。选择保留周期是存储成本与可回溯深度的权衡。
为何常见:演示环境里数据量小,看不出成本;生产环境 TB 级数据下,无限保留会让存储账单迅速失控。
误区乐观并发控制反正会检测冲突并自动重试,那让写任务无限循环重试没关系,反正最终会成功。
正解重试确实能解决偶发冲突,但如果同一个数据点被高频并发写入,冲突会持续发生,任务可能陷入长时间的无效重试,甚至导致活锁——永远有人冲突、永远有人失败。此时应改用 Merge-on-Read 减少单次写入覆盖范围,或对热点数据做分桶、增加预处理合并,而不是让系统盲目重试。
为何常见:直觉上「失败-重试」是万能的,忽略了高冲突场景下重试成本呈指数上升的现实。
误区Iceberg、Delta、Hudi 都是实现 ACID 的组件,那我可以在同一张表上随意切换,甚至混合使用。
正解这三者是相互独立的表格式实现,各自定义了自己的元数据组织、事务日志格式和 API 规范。一张表只能由一种系统管理,切换意味着数据重写和迁移。选型时应结合计算引擎生态(如 Delta 对 Spark 友好)、写入模式(Hudi 对增量友好)、以及开放性需求(Iceberg 最中立)来决定,而不是当成可插拔的插件随意更换。
为何常见:初学者看到它们功能相似,误以为像数据库驱动一样可以随时替换,忽略了底层文件组织方式的根本差异。
面试题
Q1. 数据湖最初在对象存储上最突出的数据问题是什么?
- A.无法保存海量非结构化数据
- B.并发读写缺少事务保护,会产生覆盖和不一致← 正确答案
- C.对象存储的容量太小
- D.不支持列式存储格式
答案:B。对象存储只支持整体写入和覆盖,没有并发控制,多个作业同时读写时后写者会覆盖先写者,读者可能读到半成品。这正是事务层要补的短板。A、C 与事实相反,D 说的是文件格式问题而非一致性问题。
Q2. 快照隔离给读者带来的核心保证是什么?
- A.每次查询都能看到最新提交的数据
- B.在读取期间看到固定版本的数据视图← 正确答案
- C.读者之间可以互相看到对方的未提交修改
- D.写者可以强制刷新读者的缓冲区
答案:B。快照隔离让读者在事务开始时获得一个版本指针,整个读取过程都基于该指针解析文件,不受后续提交影响。A 属于读已提交的语义,C 是脏读,D 在数据湖场景中并不存在。
Q3. 在 Copy-on-Write 写入路径中,被修改的旧文件会怎样处理?
- A.立即物理删除以释放空间
- B.原样保留,继续被旧快照引用← 正确答案
- C.原地覆盖为新内容
- D.合并到另一个旧文件
答案:B。COW 的核心是「改副本不动原件」。旧文件仍然被旧快照的清单引用,因此必须原样保留,这也正是时间旅行能力的基础。A、C 会破坏快照一致性,D 是合并策略而不是 COW 本身的处理方式。
Q4. 以下哪些属于 ACID 事务的四个性质?
- A.原子性(Atomicity)← 正确答案
- B.一致性(Consistency)← 正确答案
- C.分布式(Distributed)
- D.持久性(Durability)← 正确答案
答案:ABD。ACID 指原子性、一致性、隔离性(Isolation)和持久性。C 的「分布式」是系统架构属性,不属于事务性质。本题考察对 ACID 定义的准确记忆,容易把相关但不同维度的概念混入。
Q5. 乐观并发控制提交时发现 V_commit ≠ V_base,正确的处理方式是?
- A.强制覆盖并提交,数据以本事务为准
- B.丢弃写入并一直等待其他事务释放锁
- C.放弃本次提交,基于最新版本重新计算后再次尝试提交← 正确答案
- D.将目标数据临时改为只读
答案:C。乐观并发控制不加锁,冲突时让失败方基于最新版本重试是标准做法。A 会造成丢失更新,B 中乐观控制根本没有锁可等,D 改变了数据权限语义。实际系统中还会设置重试上限以避免活锁。
Q6. 数据湖的时间旅行能力可以被用于哪些场景?
- A.查询历史上某个时刻的数据视图← 正确答案
- B.把误操作的表回滚到事故前版本← 正确答案
- C.提高高频写入的吞吐量
- D.审计数据表随时间的演化过程← 正确答案
答案:ABD。时间旅行本质是「按版本读取快照」,因此历史查询、回滚、审计都依赖它。C 是写入模型的优化目标,与时间旅行无关,属于干扰项。
Q7. 相比 Copy-on-Write,Merge-on-Read 模式的主要代价是什么?
- A.写入速度显著变慢
- B.读取时需要合并基础文件和增量文件,查询变慢← 正确答案
- C.完全不支持快照隔离
- D.无法保存列式文件
答案:B。Merge-on-Read 把合并工作推迟到读取阶段,增量文件直接落盘,写得快但读得慢。它依然支持快照隔离(通过文件清单实现),也能保存列式格式,因此 C、D 错误,A 说反了。
Q8. 写放大系数 WA=(R+N)/N 中,R 代表什么?
- A.读取查询的文件个数
- B.因复制旧文件而额外写入存储的字节数← 正确答案
- C.保留的历史快照数量
- D.每笔事务的日志条数
答案:B。R 是重写旧文件的字节量,N 是新增有效数据字节量。WA 越接近 1 说明复制开销越小。A 是读路径指标,C 与版本保留有关,D 是元数据指标,均不参与写放大计算。
Q9. 以下哪些场景不适合使用乐观并发控制?
- A.读多写少的报表查询场景
- B.同一数据点被极端高频并发写入← 正确答案
- C.必须严格保证写入串行顺序的核心账本系统← 正确答案
- D.每批写入都基于最新快照的批处理任务
答案:BC。乐观并发控制假设冲突很少,适合读多写少(A)。B 中高频冲突会导致大量重试和活锁;C 需要确定性的写入顺序,乐观重试无法满足;D 总是基于最新快照计算,冲突概率低,适合乐观策略。
Q10. Delta Lake 通过什么机制记录每笔事务的变更?
- A.每次提交时全量重写元数据 JSON
- B.追加式事务日志(Transaction Log)← 正确答案
- C.依赖外部分布式锁服务
- D.直接修改表的 schema 文件
答案:B。Delta Lake 的核心是追加式事务日志,每次提交往日志里追加一条记录,所有表状态从日志重建。A 不是它的做法,C 是传统数据库的加锁思路,D 只是 schema 变更的一部分,不是事务记录机制。