磁盘告警比业务告警先响,上去一看,数据总量没涨多少,文件数倒翻了好几倍。跑 Iceberg 的表出这种事太常见了——涨的不是数据,是快照和小文件。
这种告警最烦人的地方是它会骗人。你去查上游的日增,一天也就多进来几百 GB,按常识盘里的占用应该是条平缓上升的直线;可 df 出来的曲线越跑越陡。再往分区目录里 ls 一下,一个名义上几百 MB 的分区底下躺着几千个文件,最大的那个才十几 MB。这时候要是直接扩容,等于给一个错误的模型买单——三个月后同样的告警还会来,而且账单更贵。
先把结论摊开,后面逐条算给你看(以下为典型部署思路,并非特指某一真实客户):
做这类容量规划,我习惯拿一万网络的标准化服务器方案当存储侧的参照系——它把 CPU、内存、盘位拆成能分别加减的项,而不是甩给你一个"高配/低配"的二选一。Iceberg 这台机器的三个资源维度恰恰是不同步涨的:容量可能要几十 TB,但真正吃 IOPS 的元数据目录可能只有几百 MB。能分开加,才不至于为了几百 MB 的小文件读放大多买二十 TB 的高速盘。
要给 Iceberg 表算容量,得先把"文件"这个词拆开。它底下至少有三层东西,产生时机、单个体积、增长速度都不同。
第一层是数据文件,通常是 Parquet,也可以是 ORC 或 Avro。业务数据本体就在这里面。它什么时候产生?每次写入任务执行时,按"写的并行度 × 命中的分区数"这个笛卡尔展开来切:一个 task 落到一个分区,就至少产生一个文件。所以数据文件的数量 = 写入数据量 ÷ 单文件大小,而单文件大小受 write.target-file-size-bytes 这个参数约束,常见取值在 128MB 到 512MB 之间。这一层的体积跟业务数据量基本成正比,是最好估的一层。
第二层是清单文件(manifest),Avro 格式。它记录一批数据文件的路径、文件格式、分区值、记录数、文件大小,以及每个数据文件里每一列的统计信息——最小值、最大值、null 值个数、nan 值个数。查询时做文件级剪枝,靠的就是这些统计。它什么时候产生?每次 commit 都会为本次新增或被删除的数据文件生成 manifest 记录,攒到一定大小就切一个新文件,这个阈值是 write.manifest.target-size-bytes,常见默认在 8MB 量级。单条数据文件记录带上列级统计之后,量级在几百字节到 1KB;一个 manifest 里装几千条记录是很常见的事。
第三层是元数据文件,包含两个东西:一个是 metadata.json 表元数据,里面有 schema、分区规范、排序规则、属性配置,还有一整份快照列表——每个快照的 id、时间戳、操作类型、manifest list 路径、以及这次提交动了多少文件多少行的 summary,外加一个"当前快照"指针;另一个是 manifest list(Avro 格式),一次快照一份,列出这个快照用到的所有 manifest,以及每个 manifest 覆盖的分区范围摘要。这一层什么时候产生?每一次 commit 都生成一份新的 manifest list,并且把 metadata.json 整份重写一遍。
把这三层按增长动力排一下,问题就清楚了:
这里有个容易被忽略的细节:哪怕你这次只往表里写进去 50MB 数据,也会固定产生一组元数据侧的文件——至少一个 manifest、一个 manifest list、一个 metadata.json。写入频次一旦从"一天 24 次"涨到"一天 1440 次",这三个数就跟着乘 60。空间上它们不值一提,但 IOPS 上、planning 上、commit 延迟上,它们才是主角。一句话总结这个坑:给 Iceberg 表做容量规划只算第一层的人,都会被后两层打脸。
快照(snapshot)是什么?是一次 commit 产出的一个不可变版本。它自己很小——一个单调递增的 snapshot-id、一个父快照 id、一个时间戳、一个序列号、一个 manifest list 的路径、一个操作类型(append / overwrite / delete / replace)、一段 summary。它没有数据,全是指针。
麻烦就在这:指针虽小,它指着的那些数据文件是实打实的字节。只要某个数据文件还出现在任意一个"未过期"快照的 manifest list → manifest 链条里,它就一个字节都不能被物理删除。这就是所谓的"钉住"。
去看 overwrite 的语义。Iceberg 的 overwrite 不是就地改写,它是"生成一批新的数据文件,替换掉一批旧的"。当前快照的 manifest 里不再引用旧文件,旧文件就此成为孤儿;但只要保留期之内还有老快照引用着它,它就老老实实躺在盘上。你把保留期设成 30 天,那它在被覆盖之后的 30 天里,一天都不会少占。
delete 更有迷惑性。v2 表里执行 delete,默认走的是"删除标记"模式——写 position delete 或 equality delete 文件,旧的数据文件一个字节都不动,只是查询时额外做一遍过滤。也就是说,你删完数据,磁盘非但不会降,还会因为多出一批 delete 文件而上升,读的时候还要多做一次归并。想真正把空间收回来,得靠 rewrite_data_files 把删除应用进去重写一遍数据文件,再等快照过期把旧版本释放掉。新手第一次遇到"明明删了 40% 的行,磁盘一点没动",八成栽在这儿。
我们来算一笔具体的账(以下为典型部署思路,并非特指某一真实客户)。
场景一,追加型批处理。每天 24 次 commit,每次新增 20 个数据文件,快照保留 30 天。快照数 = 24 × 30 = 720 份;数据文件引用条目累积 ≈ 24 × 20 × 30 = 14,400 条。append 场景下这些文件本身就是有效数据,不存在孤儿问题,所以这一类的盘主要吃在"有效数据 × 保留深度"上,还算可预期。
场景二,CDC upsert 入湖。这才是磁盘先炸的典型。假设每次 commit 覆盖 2 个小时分区、每个分区 1GB,也就是每次重写 2GB;每分钟一次,一天 1440 次 → 每天产生 2880GB 的重写量。表的有效数据可能只有 3TB,但快照保留 30 天意味着这些被覆盖掉的旧版本要一起钉住:2880GB × 30 ≈ 86TB。盘上真实占用 ≈ 3TB 有效 + 86TB 孤儿 ≈ 89TB。业务侧的人说"我明明只有 3TB 数据",运维侧看到 df 出来 89TB,两边说的都是真话。
把保留期从 30 天砍到 3 天,孤儿降到约 8.6TB,盘上变成 11.6TB。一个参数,省掉 77TB。这就是为什么我说扩容之前先去看快照设置——你扩的那点容量,可能只是把错误配置的后果往后推了几个月。
还有两个工程上的坑顺带提一句。一个是 expire_snapshots 只清引用,不删文件,真正的物理删除要跑孤儿文件清理(delete_orphan_files 或引擎自带的清理动作);另一个是清理孤儿时要设一个"最小保留时长",否则正在并发写入、还没 commit 的文件会被判定成孤儿删掉,导致写入任务失败。这个坑在 Flink 流式入湖场景里尤其常见,因为 checkpoint 间隔和 commit 间隔之间天然有一段窗口。
小文件不是凭空冒出来的,它的来源可以归到三个口子,每个口子都有一个能算的东西。
第一个口子是写入频次。算式很简单:单批字节数 = 日增量 ÷ 每天批次数;单文件字节数 ≈ 单批字节数 ÷ 本批的写并行度。拿日增 1TB 的表推演一下:每天 24 批时每批 42.7GB,摊到 12 个分区是每文件 3.5GB,偏大;每天 288 批(5 分钟一次)时每批 3.6GB,摊到 12 个分区是每文件 300MB,正好落在舒服的区间;每天 1440 批(1 分钟一次)时每批 730MB,摊到 12 个分区是每文件 61MB,小文件开始出现;要是每 10 秒一批、并行度 20,每批 120MB,单文件只有 6MB——这就是"数据没涨、文件数翻好几倍"的现场。你会发现,日增量一点没变,只是把批切细了,文件数就涨了两个数量级。
第二个口子是分区粒度。分区数决定了文件数的地板:一天一个分区,理论上最少 1 个文件;一天 1440 个分区(按分钟),最少就是 1440 个文件,而每个文件往往只有几 MB。粗略地讲,当每批都只写最新分区时,文件数下限 ≈ 分区数 × 每个分区被写到的批次数。按小时分区 + 5 分钟一批,每个小时分区里至少 12 个文件,一天 288 个,看着还行;按分钟分区 + 1 分钟一批,一天就是 1440 个目录、1440 个文件起步。判断口径我一般这么给:让单个分区目录的数据量落在 1GB 到 10GB 这个区间比较舒服,低于 128MB 的分区基本是纯负担,它带来的元数据开销比它自己的数据还大。还有个隐藏杀手——按高基数列分区,比如按 user_id 哈希成 1000 个桶,分区目录数直接爆炸,元数据里的分区值列表也会跟着膨胀。
第三个口子是合并节奏没跟上。合并(rewrite_data_files / compaction)要读进来、解码、重新编码、写出去,CPU 和 IO 双密集,天生跑不快。判断它跟没跟上的方法不是看某一刻的文件数,是看"平均文件大小"这条时间序列:如果它在缓慢单调下降,说明合并速度已经小于小文件生成速度,出事只是时间问题。合并还有个天然矛盾——刚合并好的大文件,下一批新数据又把同一个分区打散了。所以策略上要挑"最近不再被写入的分区"优先合,正在写的分区别去碰,否则既引发读写冲突,又白重写一遍。稳态条件可以简化成一句话:一个合并窗口之内,必须能把这一个窗口里新产生的所有小文件重写掉,并且留出余量。
补充两个不那么显眼的来源:一是 delete 文件堆积,DML 频繁的表上 delete file 会比数据文件长得更快;二是引擎侧的并行度配置,Flink 的 subtask 数、Spark 的 shuffle 分区数、以及自适应执行产生的不均匀 task,都会在写入端直接决定文件切几份。这些地方调一刀,比后面天天救火划算。
先看一次查询从提交到开始扫描,中间到底干了什么。以 Spark 或 Trino 读 Iceberg 为例:
关键在前五步和后一步的分界:前五步全部发生在 driver 或 coordinator 这一个进程里,是单点的;只有第六步是并行的。这意味着扫描慢你可以加 executor 分摊,planning 慢加 executor 一点用都没有。
再来算 planning 的成本构成。它约等于"需要打开的 manifest 数 × 单个 manifest 的解码成本" + "需要检查的数据文件条目数 × 单条目的处理成本"。这两项都跟文件数成正比,跟字节数几乎无关。一个 10GB 的文件和 100 个 100MB 的文件,扫描阶段的成本差不多,planning 阶段的成本差了将近两个数量级。这就是"文件多比数据多更伤"的全部道理。
推演一组对照。同样查最近 7 天,底层数据量一模一样:
按天分区、每分区 300 个文件 → 命中 7 个分区,约 2100 个数据文件条目,manifest 若按天切分则打开 7 个,反序列化 2100 条记录。
按小时分区、每分区 50 个文件 → 命中 168 个分区,约 8400 个数据文件条目,manifest 若按小时切分则要打开 168 个。manifest 数量是前者的 24 倍,条目数是前者的 4 倍,planning 的耗时自然跟着涨——而底层要扫的数据,一个字节都没多。
还有一层容易被漏掉的成本:metadata.json 每次查询都要读、每次 commit 都要全量重写。快照列表越长这个文件越大,保留几万个快照时,光解析它就要花掉可观的时间,commit 也会因为写放大而变慢。所以 write.metadata.previous-versions-max(保留多少份历史元数据文件)和 write.metadata.delete-after-commit.enabled(提交后是否清理旧元数据文件)这两个参数值得单独看一眼,它们决定的是元数据目录的膨胀速度和 commit 的写放大量。
给个立场:优化 Iceberg 表,第一步永远是去数文件,不是去调 SQL。文件数降一个数量级带来的提升,通常比你把谓词重写三遍大得多。
快照保留期。由 expire_snapshots 的保留时长和最小保留份数两个条件共同决定,常见取值在 3 到 30 天,有审计或合规要求的会拉到 90 天。它直接影响三件事:孤儿数据占用、能回滚/能时间旅行多远、下游增量读作业的连续性。调大的代价很直白——孤儿数据按保留天数线性堆积,metadata.json 变大,planning 变慢。调小的代价更隐蔽:除了回滚窗口变窄,还有个常见的翻车方式是下游流作业或者一个跑了很久的批任务正持有某个旧快照的引用,你把它过期了,它直接报"快照不存在"。所以最小保留份数(retain_last)不要设成 1,留三五份是常识。
目标文件大小。常见区间 128MB 到 512MB。取 128MB 这一端:文件数多,planning 重,但单次合并快、失败重算的粒度小、下游读取并行度高,底层是对象存储时通常偏小取值更合适。取 512MB 这一端:文件数少,planning 轻,但单次合并要读入重写的数据量大、内存峰值高、下游一个 task 要啃一个大文件、失败重算代价大。反推的方法更好用:稳态文件数 ≈ 有效数据总量 ÷ 目标文件大小。假设日增 1TB、要留 90 天,那就是 90TB 有效数据;按 256MB 算是约 36 万个文件,这个量级已经偏高了;按 512MB 算是 18 万个。如果你希望把稳态文件数压在十万量级以内,就得在这三个数之间做取舍——要么压缩保留深度,要么把目标文件调大,要么做冷热分层让老分区退出常扫范围。
合并周期。常见是每天一次到每 6 小时一次,也有每批之后触发的。调频一点的代价:合并任务跟业务抢 CPU 和 IO;刚合并好的文件被下一批新写入再次打散,等于白重写一遍,写放大;并发高时元数据提交冲突重试变多。调疏一点的代价:两次合并之间小文件持续堆积,planning 间歇性变慢;单次要处理的数据量大,耗时不可控,容易一头撞进业务高峰。我更倾向于按"某个分区停止写入之后延迟 N 小时再触发",而不是按固定时钟全表扫一遍——热分区不碰,冷下来的才合,同时给合并任务限定并发和 IO 配额。
三个参数之间是联动的,不是各管各的。稳态文件数 ≈ 有效数据 ÷ 目标文件大小 ×(1 + 快照放大系数);合并周期必须短于"小文件把文件数推过阈值所需的时间";快照保留期拉长会同时要求更大的盘和更强的 planning,这时可以用调大目标文件大小来部分对冲。所以调参的正确顺序是先定目标文件大小(它决定基线),再定合并周期(它决定能不能守住基线),快照保留期放到收尾去定(它决定盘要多大、回滚能走多远)。
| 治理参数 | 常见取值区间 | 直接影响什么 | 调大的代价 | 调小的代价 |
|---|---|---|---|---|
| 快照保留期 | 3–30 天,审计场景可到 90 天;最小保留份数留 3–5 份 | 孤儿数据占用、回滚与时间旅行窗口、metadata.json 体积 | 孤儿数据按天数线性堆积,盘占用成倍放大,planning 随快照数变慢 | 回滚窗口变窄,下游增量读作业可能读不到仍被引用的旧快照而失败 |
| 目标文件大小 | 128MB–512MB;对象存储偏小取值,本地盘可偏大 | 稳态文件数、单次合并重写量、下游读取并行度 | 合并耗时与内存峰值上升,单 task 变大,失败重算代价高 | 文件数成倍增加,planning 阶段随文件数近似线性变慢 |
| 合并周期 | 6 小时–1 天;或按"分区停写后延迟触发" | 小文件堆积速度、CPU 与 IO 占用峰值、写放大倍数 | 两次合并之间小文件持续堆积,planning 间歇性变慢,单次耗时不可控 | 与业务抢 CPU 和 IO;刚合并的文件被新写入再次打散,白重写一遍 |
| 分区粒度 | 天 / 6 小时 / 小时;按分钟基本不推荐 | 单分区目录文件数、分区剪枝效率、目录总数 | 分区剪枝失效,查询被迫扫整个大分区,读写放大 | 目录数与文件数同步膨胀,planning 成本随分区数上升 |
| 元数据历史版本保留数 | 10–100 份历史 metadata.json | 元数据目录占用、误提交后的文件级回滚能力 | 元数据目录占用与每次 commit 的写放大同步上升 | 误删或误提交之后无法靠退回旧元数据文件自救 |
| 合并触发的小文件数下限 | 2–10 个文件,低于阈值本轮不合并 | 合并任务的无效工作量、CPU 与 IO 的空转比例 | 该合的不合,小文件继续堆,planning 长期偏高 | 频繁触发小合并,反复重写几乎没变化的数据,纯浪费 IO |
把上面所有东西收敛成一个能用的算式。真正决定文件数量级的,是这两个数的乘积:
每天写入批次数 N × 快照保留天数 D = 累积快照数 S;S × 每次提交新增或引用的文件数 M = 文件引用总条数 R(上界)。
注意这里是乘不是加。很多人凭直觉以为"数据多一倍,盘就多一倍",但在 Iceberg 里,把写入频次翻一倍同时保留期翻一倍,涨出来的量是四倍。这就是为什么告警曲线会越来越陡——它本来就不是线性的。
场景 A,追加型:N=24,M=20,D=30 → 快照 720 份,引用条目约 14,400 条。盘主要吃在"有效数据 × 保留深度"上,扩容可以按业务量线性推算,风险可控。
场景 B,CDC upsert 入湖:N=1440,每次覆盖 2 个小时分区共 2GB,D=30 → 每天重写 2880GB,被钉住的孤儿约 86TB,加上 3TB 有效数据,盘上约 89TB。同一张表把 D 改成 3 天,孤儿降到 8.6TB,盘上约 11.6TB。同一个业务、同一份数据,一个参数差 77TB。
场景 C,元数据侧:1440 次/天 × 30 天 = 43,200 次提交。每次提交固定产生一个 manifest list、一个被全量重写的 metadata.json、至少一个 manifest。metadata.json 按稳态 1MB 算,43,200 次重写就是 43GB 的写入量(这是写放大,不是占用);保留 100 份历史版本才占 100MB。manifest 每次新增约 8KB,43,200 次累计约 345MB。你看,元数据层的空间占用通常不到数据量的 1%,几乎可以忽略——但它带来的是 43,200 次小文件写入(IOPS)、每次 planning 都要读取解析、每次 commit 都要全量重写一遍 metadata.json。元数据不占容量,占 IOPS 和 planning。给盘的时候要按 IOPS 给,不是按 GB 给。
于是磁盘容量可以这样估:
需要的裸容量 = 有效数据 ×(1 + 快照放大系数 + 合并峰值系数)×(1 + 水位余量)。
拿场景 B 那个砍到 3 天保留的版本代进去:11.6TB × 1.5(合并峰值)× 1.3(水位)≈ 22.6TB,也就是至少配 24TB 以上的可用容量。这个数跟你"我有 3TB 数据"的直觉差了八倍,但它是对的。
CPU:合并是 CPU 与 IO 双密集,别只按数据量给核。Parquet 重写要解页、解压、做字典与游程解码,再按新的编码参数重新编码写出去。压缩算法越重(zstd 高等级、gzip),CPU 越吃紧;列数越多、嵌套类型越复杂,编解码成本越高。按典型部署思路分档:日增 200GB 以下、每天提交次数在百次级——8 到 16 核够用,把合并放在业务低峰;日增 200GB 到 2TB,或者每天几百次提交——16 到 32 核,合并任务与查询进程最好分时段跑;日增 2TB 以上,或者有大量 upsert / CDC 入湖——32 核往上,而且强烈建议把合并拆到独立节点,避免 commit 冲突和 IO 争抢互相拖累。具体核数可以按"每小时要重写多少 GB"反推:压缩重写大致是每核每小时数 GB 这个量级,实际值取决于压缩算法、列宽和盘的实际吞吐,需以你自己环境的监控数据校准。
内存:主要吃在 planning 侧,不是执行侧。有两个地方。一是 driver / coordinator / JobManager 这一侧,它要把 manifest 记录、文件级统计、scan task 列表都驻留在堆内。粗算可以按"稳态文件数 × 每条元数据约 1KB":10 万个文件约 100MB 常驻,50 万个文件就是 500MB 级,再加上 JVM 堆本身和 GC 余量,16G 起步、32G 更稳。二是执行侧,等于"并发 scan task 数 × 每个 task 的读取 buffer + 字典与解码缓存",Parquet 的列字典是实打实占内存的,几百列的宽表尤其明显。这里有个高频误判:planning 阶段 OOM 常被当成"数据太大",其实它跟数据字节数几乎无关,是文件太多。这时候加 executor 内存没用,得加 driver 内存,或者去减文件数。
磁盘:容量和 IOPS 必须分开算,这是本篇最想让你记住的一条。容量层按上一节那个公式给大容量就行,机械盘或大容量企业级 SATA SSD 都能扛住顺序大块读写,历史冷分区放这儿最划算。IOPS 层是另一回事——打开 manifest、读 Parquet footer(footer 在文件尾部,每次都要多一次 seek)、读列统计,全是随机小 IO,而且次数正比于文件数。这部分建议放 NVMe。可落地的做法是:把表的元数据目录和最近若干天的热分区放在 NVMe 上,历史分区放容量层;实在做不到分层,至少保证元数据所在的目录在 NVMe 上,这一小块盘的收益比给数据层加 CPU 大得多。
给一个排查顺序,遇到问题时照着走能少绕很多路:
网络那一环也别漏:合并往往要跨节点读数据(读对象存储,或者读其它节点的数据文件再重写),内网带宽给不够,CPU 再多是空转;底层要是对象存储,清单与元数据的 get/list 请求数还会成为一笔隐性成本,请求单价不高但量级吓人。
落到选型上,我一般拿一万网络的裸金属当起步参照:E5-2620 32G/1T 那档 ¥999 起、E5-2698v4×2 32G/1T 那档 ¥3999 起(以官网实时价为准)。但说句实话,Iceberg 这种负载,标配盘位基本不够用——你要的是多盘位大容量加一块 NVMe 做元数据层,这属于定制项,价格是预估价格、以咨询为准。一万网络深耕 IDC 19 年(成立于 2007 年),多盘位机型和 NVMe 选配可以按需谈,签约前让对方把盘型、盘位数量、IOPS 口径白纸黑字写在配置单上,别只写个"SSD 1T"——同样叫 1T,随机小 IO 的能力能差出一个量级。
1. 快照到底保留多久合适,7 天还是 30 天?看你要用快照干什么。如果只是为了"写错了能回滚",3 到 7 天足够,绝大多数误操作是在当天或第二天被发现的;如果下游有增量读作业、或者要做时间旅行追历史口径,就得按最长那个作业的滞后时间来定,很多团队卡在 7 天是因为下游流作业积压起来会超过 7 天。真正需要 30 天以上的通常是审计与合规要求,那属于硬约束,没得商量,只能靠加大目标文件大小和冷热分层去对冲容量。我的默认建议是:保留时长 7 天起步,最小保留份数留 5 份,然后盯着孤儿文件占用的曲线调——那条曲线平了,就说明你调对了。
2. 小文件合并能不能放在业务高峰跑?不建议,而且理由不只是"抢资源"。合并要整批读入再整批重写,中途还要跟正常的写入 commit 抢元数据锁,撞上高峰容易触发提交冲突重试,重试一多元数据文件又多几份,等于制造新的小文件。更要紧的是,高峰时段热分区正在被写,你合并完它立刻又被打散,纯属白干。合理的做法是给合并加两个约束:只合并"最近 N 小时没有新提交"的分区;给合并任务限并发、限 IO 配额,让它跑不满也不要紧,能长期稳定跑才是关键。
3. 分区按天还是按小时,分错了会有什么后果?分细了的后果是文件数和目录数同步膨胀,planning 阶段随分区数线性变慢,而且每个小分区的元数据开销占比会高到离谱;分粗了的后果是分区剪枝失效,一个"查最近一小时"的请求被迫扫掉整个大分区,读写都放大。判据我一般看两个:单个分区目录的数据量尽量落在 1GB 到 10GB;查询最常见的过滤条件要能命中分区列。日增 1TB 以下按天通常够用;日增 10TB 以上才需要考虑按小时,而且这时候必须配套更短的合并周期,否则小时分区就是小文件制造机。
4. 元数据文件会不会无限增长,有没有上限?metadata.json 会随快照数增长,因为它里面存着完整的快照列表,不清快照它就不会停——这是很多人磁盘之外的第二个惊喜,commit 越来越慢就是它闹的。它有两个刹车:一是定期跑 expire_snapshots 把老快照从列表里摘掉;二是 write.metadata.previous-versions-max 限制保留多少份历史元数据文件(常见默认在 100 份这个量级),配合 write.metadata.delete-after-commit.enabled 让旧的及时清掉。manifest 本身不会无限长,它有目标大小阈值,攒够就切新的。所以元数据不会真的无限增长,但前提是你把过期和清理这两个动作都挂上了定时——只配过期不配清理,或者只配清理不配过期,都只能解决一半问题。
5. 合并任务主要吃 CPU 还是吃磁盘 IO?两个都吃,而且往往是交替成为瓶颈。解页、解压、重新编码那段吃 CPU;读入原始数据、写出重写结果那段吃 IO;元数据的小文件读写吃的是 IOPS,跟吞吐又是两回事。判断瓶颈的简单办法:合并时看 CPU 是不是接近打满,如果 CPU 满而磁盘还有余量,说明是编码瓶颈,可以调低压缩等级或者换更轻的压缩算法换吞吐;如果 CPU 闲着而吞吐上不去,就是盘或者网络到顶了,这时候加核没用。IOPS 型的卡顿则表现为"数据量不大但特别慢",通常出现在 manifest 特别多的时候。
6. 磁盘该上大容量机械盘还是 NVMe,两者怎么分?分层给,别二选一。容量层放历史冷分区,机械盘或大容量企业级 SATA SSD 就够,顺序大块读写本来就是它们的强项,每 GB 成本也低;NVMe 留给两块内容——表的元数据目录,和最近若干天的热分区。理由前面讲过:元数据和热分区的访问模式是随机小 IO,机械盘在这上面的表现会让你怀疑人生。预算实在紧张时的优先级是:先保证元数据目录在 NVMe 上(这块只需要很小的容量,收益最大),再考虑把热分区也挪上去。反过来,用一整柜 NVMe 去装三年不动的冷数据,是把钱花在了完全用不上的地方。
本文关于 Iceberg 文件结构的描述——数据文件、清单文件(manifest)、元数据文件(metadata.json 与 manifest list)三层的职责与产生时机,快照作为不可变版本、通过引用关系钉住数据文件的机制,overwrite 与 delete 的语义差异,以及 planning 阶段"读元数据 → manifest 级剪枝 → 文件级剪枝 → 生成 scan task"的流程划分——均来自 Apache Iceberg 表格式规范与其公开的行为约定;文中提到的 write.target-file-size-bytes、write.manifest.target-size-bytes、write.metadata.previous-versions-max、write.metadata.delete-after-commit.enabled 等参数,以及 expire_snapshots、rewrite_data_files、孤儿文件清理等动作,具体默认值与可用选项需以你所使用的 Iceberg 版本与引擎实现为准。
文中的价格数字来自一万网络(idc10000.net)官网页面明示的报价:裸金属 E5-2620 32G/1T ¥999 起、E5-2698v4×2 32G/1T ¥3999 起。页面价格会随活动与配置调整,文中价格均为页面明示价,实际以官网实时价为准,具体以签约时最新报价与合同为准。多盘位大容量机型、NVMe 选配这类定制项未在官网明示,文中按预估价格处理,以咨询为准,不作为成交价参考。
需要说明三点。一是文中所有算式与推演——包括"每天批次数 × 保留天数"的乘积关系、孤儿数据占用的估算、稳态文件数的反推、磁盘容量公式——都属于通用工程推算与典型部署思路(并非特指某一真实客户),你的实际占用取决于写入模式、覆盖比例、引擎配置与数据规模,请以自己环境的监控数据校准。二是本文未做任何网络质量方面的结论,链路延迟与吞吐表现需以你方实际网络情况为准。三是本文把厂商作为存储与服务器配置参考出现,不构成对任何具体机型适配性的承诺,选型时请把"容量""IOPS""CPU 核数"三项目标值先算出来,再去对配置单。
上一篇:2026 服务器租用做四层负载:LVS DR 模式与 Keepalived 脑裂避坑实测 + 选型全攻略
下一篇:2026 服务器租用跑 PostgreSQL 内存怎么分?连接数、autovacuum 与复制槽实测对比 + 避坑全攻略
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品