尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
深入剖析 ScyllaDB Commitlog 段文件格式:从文件头到碎片化条目的逐字节解析
深入剖析 ScyllaDB Commitlog 段文件格式从文件头到碎片化条目的逐字节解析【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb本文以 ScyllaDB 仓库中的 commitlog 段文件格式说明 为核心系统讲解 commitlog 段文件segment file的命名规则、V2/V3/V4 三代二进制布局、CRC 与磁盘块对齐机制、多条目multi-entry与碎片化条目fragmented entry的设计动机并结合 db/commitlog/commitlog.cc、db/commitlog/commitlog_replayer.cc 等源码说明这些格式细节在实际写盘与崩溃恢复replay路径上是如何被读写的。读完后你将能够独立解读一个CommitLog-4-id.log文件的二进制结构、理解每个校验字段的计算范围并掌握 ScyllaDB 如何用块级 CRC segment id 标签区分“真实磁盘损坏”与“未写完/回收文件残留”这两类故障。需要特别注意文档开头的强约束这也是理解全文的前提commitlog 文件格式会在不同 ScyllaDB 版本之间发生变化用户不应依赖或基于它做任何假设commitlog 文件绝不允许跨 ScyllaDB 版本混用。这份文档本身就是写给 ScyllaDB 贡献者contributor看的本文同样面向需要深入 commitlog 内部实现的读者。一、文件名结构Prefixversion-id.log文档首先给出了段文件的命名方案它是一个“带版本号、按时间索引”的方案Prefixversion-id.log三个组成部分的含义组成部分含义Prefix应用相关前缀通常是CommitLog-当文件是被回收复用recycled时则为Recycled-CommitLog-version文件格式版本见下文 V2/V3/V4idreplay position 的 id 部分时间戳 shard 组合在源码中这个命名方案得到直接印证。db/commitlog/commitlog.cc 定义了默认前缀const std::string db::commitlog::descriptor::FILENAME_PREFIX(CommitLog SEPARATOR);同文件的段匹配正则会识别可选的Recycled-前缀commitlog.cc#L138static const boost::regex filename_match((?:Recycled-)?([a-zA-Z] ...而“回收”路径在 commitlog.cc#L2905 处生成带Recycled-前缀的 descriptordescriptor d(next_id(), Recycled- cfg.fname_prefix);这里的“回收”指的是复用磁盘上已存在的段文件空间覆盖写而非重新分配这正是后文 V3 格式中“块内打上 segment id 标签”要解决的核心问题——回收文件的残留数据可能与新写入的数据混杂在同一块中。1.1 文件中的id时间戳 shard 位布局文档说id是 “the id part of a replay position (timestamp shard)”。在 db/commitlog/replay_position.hh 中可以看到这个 idsegment_id_type即uint64_t的位级构造方式struct replay_position { static constexpr size_t max_cpu_bits 10; // 1024 cpus. should be enough for anyone static constexpr size_t max_ts_bits 8 * sizeof(segment_id_type) - max_cpu_bits; ... replay_position(unsigned shard, segment_id_type i, position_type p 0) : id((segment_id_type(shard) max_ts_bits) | i), pos(p) { ... } };即 64 位 id 的高 10 位保存 shard 编号最多支持 1024 个 shard低 54 位保存“基础 id”源自启动后毫秒数的时间索引。完整回放位置由idposuint32_t段内偏移共同组成结构定义见 replay_position.hh#L23-L64。这解释了为什么段文件名中的数字天然带有“时间排序”性质同一 shard 上id 大的段一定更晚创建。descriptor文件名 ↔ id/version 的双向转换封装在 db/commitlog/commitlog.hh 中声明其中版本常量明确列出了 14 四代格式且当前版本为 V4static inline constexpr uint32_t segment_version_1 1u; static inline constexpr uint32_t segment_version_2 2u; static inline constexpr uint32_t segment_version_3 3u; static inline constexpr uint32_t segment_version_4 4u; static inline constexpr uint32_t current_version segment_version_4;二、段文件总体结构文件头 Chunk Entry文档对 V2 格式给出了一条总原则和三级层次所有控制数据均以网络字节序network byte order写入。文件由一个文件头file header加任意多个chunk组成。每个 chunk 自带头部并且头部中包含“指向下一个 chunk 起始位置的 marker”目的是当某个 chunk 的数据区发生损坏时能够跳过它。Chunk 内部再包含若干数据条目entry每个条目有小头 数据 校验和。此外一条 entry 还可以是multi-entry——即把多个条目合并为一条写入。用一张层次图概括依据 docs/dev/commitlog-file-format.md 的 V3 布局整理segment file ├── file header (magic, version, id, [alignment], crc) ├── chunk #0 │ ├── chunk header (file_pos → 下一 chunk 的文件位置, crc) │ ├── entry 1 (size, crc, data) │ ├── entry 2 ... │ └── (或) multi-entry (magic0xffffffff, size, crc, entries*N, crc2) ├── chunk #1 │ └── ... └── 文件末尾: uint64(0) 终止标记 (见源码 do_flush/termination 路径)源码侧的对应关系magic 常量commitlog.cc#L839static constexpr uint32_t segment_magic (S24) |(C 16) | (L 8) | C;与文档中SCLC四个字符拼出的魔数一致。multi-entry 与 fragmented-entry 的魔数commitlog.cc#L840-L841multi_entry_size_magic 0xffffffff、fragmented_entry_size_magic 0xfffffffe与文档 V2/V4 小节描述的0xffffffff (MAX_UINT32)与0xfffffffe (MAX_UINT32-1)完全对应。文件头写出commitlog.cc#L1126-L1139 中首个块off 0会依次写入 magic、version、id、alignment并对version id 低 32 位 id 高 32 位 alignment计算 CRC32与文档 V3 的“CRC32 of version, low 32 of id, high 32 of id, alignment”逐字段吻合。chunk header 写出commitlog.cc#L1141-L1149 中非终止块会写入_file_pos下一个 chunk 的文件位置以及对id 低 32 位 id 高 32 位 (off header_size)本头部结束处的文件偏移计算出的 CRC——同样是文档 V3 chunk header 描述的精确实现。文件终止commitlog.cc#L1154-L1159 显示终止块termination为真时写入一个uint64_t(0)这是文档未强调、但从源码结构可以确认的文件级结束标记。三、Version 2逐条目 CRC 的格式V2 是 ScyllaDB 1.0 起长期使用的格式文档注称为 2 是因为它是对 Cassandra 格式的微调。其布局为完整继承自 docs/dev/commitlog-file-format.md#L38-L66Segment file header magic : uint32_t - (S24) |(C 16) | (L 8) | C version : uint32_t - same as descriptor id : uint64_t - same as descriptor crc : uint32_t - CRC32 of version, low 32 of id, high 32 of id Chunk header file_pos : uint32_t - the file position of next chunk crc : uint32_t - CRC32 of low 32 of segment id, high 32 of id and file offset of end of this header Entry size : uint32_t - size of entry (data full headers), MAX_UINT32 crc1 : uint32_t - CRC32 of size data : bytes - actual entry data crc2 : uint32_t - CRC32 of size, data Multi-entry magic : 0xffffffff (MAX_UINT32) size : size of all entries in this multi-entry headers crc : CRC32 of magic, size entries * N crc2 : CRC32 of magic, size and data in each entryV2 的校验粒度是逐条目每个 entry 前后各有一个 CRCcrc1只保护 size 字段本身crc2保护 sizedata。multi-entry 则用一个外层crc2覆盖全部内部条目。写盘时 multi-entry 的头部生成逻辑可参考 commitlog.cc#L1359-L1370当writer.num_entries 1时额外写一个multi_entry_size_magic并把它纳入 CRC 计算。四、Version 3块级 CRC segment id 标签精确区分三类故障V3 相对 V2 的改动目标是文档中一句话概括的“improve error detection / false positive elimination改进错误检测、消除误报”。具体手段有两个CRC 粒度从“条目”下沉到“磁盘块”对每个写盘的块block整体做 CRC而不是按条目切分每个块打上“当前正在写的 segment id”标签以便把三类容易混淆的情况区分开——新写入的数据、回收文件里残留的旧数据、以及真正的磁盘/文件损坏。V3 布局完整继承自原文档 L77-L116Segment file header magic : uint32_t - (S24) |(C 16) | (L 8) | C version : uint32_t - same as descriptor id : uint64_t - same as descriptor alignment : uint32_t - disk block size ← V3 新增 crc : uint32_t - CRC32 of version, low 32 of id, high 32 of id, alignment Chunk header file_pos : uint32_t - the file position of next chunk crc : uint32_t - CRC32 of low 32 of segment id, high 32 of id and file offset of end of this header Entry size : uint32_t - size of entry (data full headers), MAX_UINT32 crc : uint32_t - CRC32 of size data : bytes - actual entry data Multi-entry magic : 0xffffffff (MAX_UINT32) size : size of all entries in this multi-entry headers crc : CRC32 of magic, size entries * N Disk block (block size alignment) 0 - bs - 12 : interleaved file data即上面各层内容的交错排布 bs - 12 : uint64_t - same as descriptorsegment id bs - 4 : uint32_t - CRC32 of block data up until crc包含 segment id关键点解读文件头新增alignment字段uint32_t即磁盘块大小并纳入头部 CRC。源码中该字段来自 seastar 文件的 DMA 对齐要求见 commitlog.cc#L2364align f.disk_write_dma_alignment();复用已有文件时则取disk_overwrite_dma_alignment()见 L2419/L2437。块尾部 12 字节开销每个块的最后 8 字节写入网络字节序的 segment id最后 4 字节写入覆盖“块内数据 segment id”的 CRC32。这段实现就在前文引用的写盘热路径 commitlog.cc#L1168-L1190// Build sector checksums. auto id net::hton(_desc.id); auto ss _alignment - detail::sector_overhead_size; for (auto tbuf : buf) { ... // include segment id in crc:ed data auto be p ss; be std::copy_n(reinterpret_castchar*(id), sizeof(id), be); crc32_nbo crc; crc.process_bytes(p, std::distance(p, be)); auto checksum crc.checksum(); ... // write checksum. p std::copy_n(reinterpret_castchar*(v), sizeof(v), be); }其中sector_overhead_size 128 字节 id 4 字节 crc与文档 “bs-12 / bs-4” 的偏移完全一致“块内有效载荷 alignment − 12” 也在 commitlog.cc#L1170auto ss _alignment - detail::sector_overhead_size;得到印证。故障判别逻辑文档原文给出的决策表值得完整保留块CRC 损坏→ 该文件这一段是真实损坏corrupt块CRC 正确但 segment id 不匹配→ 可推断该文件是没有写完就结束的prematurely endedid 不匹配的部分来自回收文件残留两种情况都可能造成数据丢失具体取决于写盘方式、操作系统与硬件行为。读取路径上这两个分支是真实存在的独立错误类型commitlog.cc#L3629-L3635 中CRC 不匹配抛出segment_data_corruption_errorchecksums do not matchid 不匹配则抛出 IDs do not match 的截断类错误。这些异常最终被 replay 器分类统计见 commitlog_replayer.cc#L200-L212} catch (commitlog::segment_data_corruption_error e) { s-corrupt_bytes e.bytes(); } catch (commitlog::segment_truncation e) { s-truncated_at e.position(); } catch (commitlog::header_checksum_error) { s-broken_files; }即损坏字节数、截断位置、头部校验和坏文件数被分别计入恢复统计stats而不是笼统地“失败”。五、Version 4碎片化条目让超大 mutation 能跨段落盘V4 相对 V3 的增量改动只有一个允许碎片化数据条目fragmented data entries——把单条数据拆成流跨多个段文件写入。动机在文档中写得明确A fragmented entry is written by splitting data into sub-parts that will fit into the normal restrictions of a write (i.e.smaller than max mutation size, but also trying to fit into existing buffers as best we canto avoid wasting alignment slack).也就是说切分粒度受两个约束不超过最大 mutation 尺寸同时尽量贴合缓冲区现状避免把整段 alignment 空间的尾部浪费掉。碎片条目的头部格式完整继承自原文档 L127-L137Fragmented entry magic : fragmented marker - 0xfffffffe (MAX_UINT32-1) size : size of this fragmented entry part headers id : the stream id offset : offset of this entry in the data stream remaining : stream data remaining to write after this entry crc : CRC32 of magic, size, id, offset and remaining源码中每段碎片头部恰好 16 字节commitlog.cc#L836fragmented_entry_overhead_size 4 * sizeof(uint32_t)magic 写入与 CRC 计算见 commitlog.cc#L1393-L1398。此外还有一个重要的前置条件碎片化能力是可配置开关默认关闭commitlog.hh#L119allow_fragmented_entries false写盘前会检查该开关见 commitlog.cc#L1603。5.1 乱序重组stream id 是单调计数器replay 必须处理乱序文档强调每条流有唯一 id单调计数器由于回放replay时的顺序无法总是被保证必须能处理“乱序、交错”的流读写。replayer 必须携带一个replay_state状态存储来处理碎片条目——遇到碎片就按其id存入状态缓冲直到id offset remaining三个字段表明所有碎片到齐才向调用方上报完整条目。这个replay_state在仓库中是真实存在的类型声明db/commitlog/commitlog.hh#L462-L473replay_state是commitlog的内部类Pimpl且read_log_file提供带replay_state的重载版本static future read_log_file(sstring filename, sstring prefix, commit_load_reader_func, position_type 0, ...); static future read_log_file(const replay_state, sstring filename, sstring prefix, commit_load_reader_func, position_type 0, ...);恢复入口db/commitlog/commitlog_replayer.cc#L176-L212 中recover()接收const commitlog::replay_state并把“逐块读 逐条 process”的回调传给db::commitlog::read_log_file(rpstate, ...)在跨段重放循环里还会维护每个 shard 的碎片状态表commitlog_replayer.cc#L396 附近的std::unordered_mapunsigned, commitlog::replay_state states。5.2 回放路径的整体流程结合 commitlog_replayer.cc#L223-L280一条被读回的 mutation 条目在重放前要经过多级“位置过滤”这与段文件里id 时间戳shard的编码直接相关若条目 rp 小于该 shard 的全局最小回放位置min_pos来自 system 表记录的位置元数据跳过skipped_mutations若小于该表cf在对应 shard 的最小位置跳过若小于该 token range 的最小位置跳过否则按条目携带的 schema version 解析必要时使用条目内嵌的 column mapping跨 shard 投递并 apply。这意味着段文件“按 shard 分片、id 内嵌 shard 号”的命名/编码方式是回放时高效跳过已持久化数据的前提——文档中id (timestamp shard)这一句在此找到了完整的工程意义。六、配置与格式版本的关系把文档格式说明放回 ScyllaDB 的配置体系中有几个值得注意的对应点均来自 db/commitlog/commitlog.hh#L89-L132 的commitlog::config配置项默认值与文件格式的关联commitlog_segment_size_in_mb32段文件大小决定一个 chunk/块序列的规模commit_log_location—段文件存放目录启动时按文件名正则扫描max_reserve_segments12预分配保留段数量上限不可经 scylla.yaml 配置fname_prefixdescriptor::FILENAME_PREFIX即CommitLog-对应文件名中的Prefix回收时拼接Recycled-descriptor_tag空非空时文件名为CommitLog-4-id.tag.log如variant条目格式由 tag 决定allow_fragmented_entriesfalse是否启用 V4 的碎片化条目base_segment_id无按启动后毫秒数计算新段 id 从此值“之后”顺序发出存在更大的待重放段时取最大者之后另外read_log_file支持从任意position_type开始读position_type即段内偏移这也是 chunk header 里file_pos指针能够“跳过损坏 chunk”的读取侧支撑恢复代码在读文件时按 alignment 对齐推进块边界commitlog.cc#L3570-L3618逐块校验 CRC 与 id遇到unexpected EOF或校验失败即抛出带位置的异常由上层决定截断还是跳过。七、给贡献者的实践要点综合文档与源码若要动手调试 commitlog 段文件建议按以下顺序进行确认版本先读文件头 4 字节应为SCLC魔数(S24)|(C16)|(L8)|C随后读 version 判断 2/3/4。当前代码只生成 V4current_version segment_version_4读取侧对 V3 头部含alignment字段有专门的解析分支见 commitlog.cc#L3527-L3562。用 alignment 推算块边界V3 的每个磁盘块尾部固定 12 字节id crc有效载荷为alignment - 12alignment来自磁盘 DMA 对齐要求而非固定 512/4096务必以文件头读出的值为准commitlog.cc#L1619 中仅在无段信息时才回退到 512 的保守估计。区分三类故障CRC 错 损坏CRC 对但 id 不对 未写完或回收残留两者都可能出现于同一文件的不同块恢复统计会分别累计corrupt_bytes/truncated_at/broken_files。碎片条目要带状态读任何自行实现读取逻辑的工具遇到 magic0xfffffffe时必须维护 per-stream 状态id 单调计数器 offset remaining不能假定碎片按序到达。牢记边界约束所有控制数据为网络字节序entry size 必须小于MAX_UINT32格式随版本演进V2→V3 加块级 CRC/alignmentV3→V4 加碎片化任何离线工具都应显式声明自己支持的版本范围且不要跨版本复用 commitlog 文件。参考文件索引路径说明docs/dev/commitlog-file-format.md本文核心依据段文件命名与 V2/V3/V4 二进制格式定义db/commitlog/commitlog.cc段写入/读取实现magic、头部 CRC、块级 idCRC、multi/fragmented 条目db/commitlog/commitlog.hhcommitlog类、descriptor版本常量、replay_state、配置结构db/commitlog/replay_position.hhreplay_position的 shard时间戳位布局db/commitlog/commitlog_replayer.cc启动恢复按 shard/表/token 过滤重放条目、统计损坏与截断适用前提与限制以上解析基于当前仓库中的实现当前版本仅生成 V4 格式current_version 4V2/V3 布局主要来自 docs/dev/commitlog-file-format.md 的历史说明阅读旧版本段文件时应以文档为准。commitlog 文件格式不承诺跨版本稳定本文所有“源码行号级”结论仅对当前仓库快照有效。【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

数据驱动MPC与机组组合优化:预测、滚动求解与Matlab实现

数据驱动MPC与机组组合优化:预测、滚动求解与Matlab实现

简介:针对电力系统机组组合与模型预测控制交叉方向,这份Matlab项目案例提供了完整可运行的代码框架,适合自动化、电气工程、人工智能等相关专业学生与研究人员用于学习或二次开发。资源共29个文件,核心为16个.mat数据文件与11个.m…

📅 2026/9/14 7:50:46
MATLAB计算太阳天顶角:从赤纬、时角公式到完整实现

MATLAB计算太阳天顶角:从赤纬、时角公式到完整实现

简介:SolarAngle.MATLAB 是一份面向太阳能工程、气象与环境科学研究者的 MATLAB 计算工具,用于根据地理位置、日期和时间精确求取太阳天顶角、太阳高度角与方位角,为光伏电站朝向优化、建筑采光设计及辐射分析提供基础数据。太阳天顶角与高度…

📅 2026/9/14 7:50:46
Matlab实现区域能源系统双层优化与需求响应

Matlab实现区域能源系统双层优化与需求响应

1. 项目背景与核心价值区域综合能源系统(RIES)作为能源互联网的重要载体,正在推动传统能源系统向低碳化、智能化转型。这个Matlab复现项目源自核心期刊论文,聚焦"需求响应双层优化"这一前沿方向,其核心价值在…

📅 2026/9/14 7:50:46
MORE NEWS

更多资讯

📰

SurfSense 实体信号审计清单:47 项信号驱动的品牌实体与知识图谱优化实战

SurfSense 实体信号审计清单:47 项信号驱动的品牌实体与知识图谱优化实战 【免费下载链接】SurfSense Open-source NotebookLM alternative. Research the open web with live data(Reddit, YT, IG, TikTok, Indeed, Google Search, Maps etc) through one platform…

📰

Wasp Starter Templates 实战指南:用 `wasp new` 一键搭建全栈应用脚手架

Wasp Starter Templates 实战指南:用 wasp new 一键搭建全栈应用脚手架 【免费下载链接】wasp The batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away co…

📰

edge-tts 文本转语音:免费调用微软语音合成的 3 步完整指南

edge-tts 文本转语音:免费调用微软语音合成的 3 步完整指南 【免费下载链接】edge-tts Use Microsoft Edges online text-to-speech service from Python WITHOUT needing Microsoft Edge or Windows or an API key 项目地址: https://gitcode.com/GitHub_Trendin…

📰

Flipper Zero AppManifests 完全指南:application.fam 清单格式与 fbt 构建系统深度解析

Flipper Zero AppManifests 完全指南:application.fam 清单格式与 fbt 构建系统深度解析 【免费下载链接】flipperzero-firmware Flipper Zero firmware source code 项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware Flipper Zero 固…

📰

从快速上手到三层弹性体系:OmniRoute 仓库 CLAUDE.md(巴西葡语版)技术指南深度解读

从快速上手到三层弹性体系:OmniRoute 仓库 CLAUDE.md(巴西葡语版)技术指南深度解读 【免费下载链接】OmniRoute Never stop coding. Free MIT AI gateway: one endpoint, 352 providers (150 free), 1200 models Kimi, Claude, GPT, Gemini, …

📰

深入剖析 ScyllaDB Commitlog 段文件格式:从文件头到碎片化条目的逐字节解析

深入剖析 ScyllaDB Commitlog 段文件格式:从文件头到碎片化条目的逐字节解析 【免费下载链接】scylladb NoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB 项目地址: https://gitcode.com/GitHub_Trending/s…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬