ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

RocksDB Write API 与 WriteBatch:从单条写入到批量原子的完整实现解析

RocksDB Write API 与 WriteBatch:从单条写入到批量原子的完整实现解析 数据库KV存储嵌入式数据库存储【免费下载链接】rocksdbA library that provides an embeddable, persistent key-value store for fast storage.项目地址https://gitcode.com/gh_mirrors/ro/rocksdb点击查看免费下载RocksDB 是嵌入式的持久化键值存储库其写路径的设计直接决定了吞吐量、原子性与崩溃安全。本文以docs/components/write_flow/01_write_apis.md为骨架围绕 RocksDB 的写入入口Put / Delete / SingleDelete / DeleteRange / Merge / PutEntity / TimedPut / Write与 WriteBatch 二进制格式展开结合db/db_impl/db_impl_write.cc、include/rocksdb/write_batch.h、db/write_batch.cc等源码讲清每次写入如何被封装、校验、序列化并最终汇入DBImpl::WriteImpl()的四大写入模式。读完你将掌握 RocksDB 写入 API 的语义差异、WriteBatch 的磁盘编码细节、写入选项的合法性约束以及低优先级写入限流的实现原理。写入入口所有写操作最终汇入WriteImpl()RocksDB 对外暴露了多种写入操作但所有单操作 API 在内部都会先构造一个WriteBatch再调用DB::Write()由DB::Write()分发到DBImpl::WriteImpl()实现在 db/db_impl/db_impl_write.cc。这些 API 的公开声明位于 include/rocksdb/db.hAPIValueType语义Put(key, value)kTypeValue插入或覆盖 keyDelete(key)kTypeDeletion墓碑tombstone覆盖该 key 的所有历史版本SingleDelete(key)kTypeSingleDeletion墓碑但只能与恰好一次Put配对DeleteRange(start, end)kTypeRangeDeletion墓碑覆盖[start, end)半开区间内的所有 keyMerge(key, operand)kTypeMerge对该 key 应用 merge operatorPutEntity(key, columns)kTypeWideColumnEntity宽列wide-column实体写入TimedPut(key, value, write_unix_time)kTypeValuePreferredSeqno携带显式写入时间戳的 Put供 compaction 使用Write(WriteBatch)混合一批操作的原子提交这些 ValueType 枚举定义在 db/dbformat.h是内部 key 的最后一个组成部分。源码注释明确警告DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk data structures不要修改这些枚举值它们被嵌入在磁盘数据结构中因此每个操作类型对应一个固定字节值例如kTypeDeletion 0x0、kTypeValue 0x1、kTypeMerge 0x2、kTypeSingleDeletion 0x7、kTypeRangeDeletion 0xF、kTypeWideColumnEntity 0x16、kTypeValuePreferredSeqno 0x18。需要特别说明的是Merge的配置依赖Merge操作要求通过ColumnFamilyOptions::merge_operator配置合并算子定义见 include/rocksdb/advanced_options.h否则该列族无法执行 Merge 语义。Delete与SingleDelete的区别普通Delete写入的墓碑会屏蔽该 key 的所有历史版本而SingleDelete只与恰好一次Put配对能够显著降低读放大与空间放大但若与多个 Put 配对会造成未定义行为。DeleteRange的区间语义一次范围删除可以覆盖大量 key无需逐 key 写墓碑但会额外产生 range tombstone 片段化管理开销。WriteBatch 二进制格式原子性的载体WriteBatch将多条操作序列化进一个二进制缓冲区成员rep_从而保证原子性要么全部成功要么全部失败。格式说明位于 db/write_batch.cc头文件注释指向该处。Header12 字节偏移大小字段说明08sequence占位符由写组 leader 在追加 WAL 前填充84count批内操作数量每条操作的变长编码循环重复字段编码说明taguint8ValueType 标识cf_idvarint32列族 ID仅 CF 前缀的操作 tag 携带keyvarint32 长度 字节用户 keyvaluevarint32 长度 字节value仅 Put / Merge / PutEntity 类操作序列号的填充时机关键Header 中的 sequence 字段初始化为 0。写组write groupleader 在把合并后的批追加到 WAL 之前通过WriteBatchInternal::SetSequence()实现在 db/write_batch.cc将分配到的序列号写入批头而各 writer 用于 memtable 插入的独立序列号赋值则发生在 WAL 写入完成之后。这一先 WAL 后 memtable 的顺序正是 RocksDB 崩溃恢复正确性的基石参见 docs/components/write_flow/index.md 的 Key InvariantsWAL must be written before memtable insertion。WriteBatch 关键字段元数据如何加速写路径WriteBatch类include/rocksdb/write_batch.h在rep_缓冲区之外还维护了若干元数据字段字段类型用途rep_std::string存放 header 所有操作的二进制缓冲区content_flags_atomicuint32_t批内操作类型的位掩码支持 O(1) 快速查询prot_info_unique_ptrProtectionInfo可选的逐条目校验和启用时每个 key 8 字节save_points_unique_ptrSavePoints保存点快照用于事务回滚content_flags_的价值它使得HasMerge()、HasDeleteRange()、HasPut()等查询可以在O(1) 时间内完成而无需扫描整个批内容。这些标志在操作插入时被设置在写路径校验阶段被检查例如下文WriteImpl()中my_batch-HasDeleteRange() row_cache的校验。定义见 include/rocksdb/write_batch.h。prot_info_逐 key 校验和可通过WriteBatch构造参数protection_bytes_per_key启用当前仅支持 0关闭与 8开启两种取值。启用后每个 key 附带 8 字节校验信息WriteBatch::VerifyChecksum()include/rocksdb/write_batch.h可校验批内数据的完整性。save_points_与事务SetSavePoint()记录批的当前状态RollbackToSavePoint()回滚到最近一次保存点PopSavePoint()弹出最近保存点include/rocksdb/write_batch.h。每个 SavePoint 保存rep_大小、元素个数与content_flags三个快照回滚即恢复这三者。此外WriteBatch还支持通过Handler子类 Iterate()include/rocksdb/write_batch.h逐条遍历批内操作这是 WAL 写入、memtable 插入、崩溃恢复回放共用的机制。WriteOptions 合法性校验进入写路径前的安检门进入写路径之前DBImpl::WriteImpl()会对请求做一系列校验源码见 db/db_impl/db_impl_write.cc。以下逐写入per-write选项组合会被拒绝条件原因sync disableWAL没有 WAL 就无法做同步落盘HasDeleteRange() row_cacherow cache 不支持 DeleteRange 的失效处理disableWAL recycle_log_file_num 0回收 WAL 的损坏检测依赖序列号顺序protection_bytes_per_key不是 0 或 8仅支持两种保护级别rate_limiter_priority不是IO_TOTAL或IO_USER实现约束对应源码片段if (write_options.sync write_options.disableWAL) { return Status::InvalidArgument(Sync writes has to enable WAL.); } if (my_batch-HasDeleteRange() immutable_db_options_.row_cache) { return Status::NotSupported(DeleteRange is not compatible with row cache.); } if (write_options.protection_bytes_per_key ! 0 write_options.protection_bytes_per_key ! 8) { return Status::InvalidArgument( WriteOptions::protection_bytes_per_key must be zero or eight); }disableWAL recycle_log_file_num的特例WritePreparedTxnDB 内部使用disableWAL做拆分写入WAL-only 的 prepare memtable-only 的 commit该场景在two_write_queues disable_memtable组合下被放行因为回收 WAL 的损坏检测依赖顺序序列号而该模式不适用此约束。源码中写得很明确Corruption detection in recycled WALs relies on sequential sequence numbers, but WritePreparedTxnDB uses disableWAL internally for split writesdb/db_impl/db_impl_write.cc。时间戳补充校验如果写入目标是 memtable!disable_memtable且批内 key 需要时间戳更新WriteBatchInternal::TimestampsUpdateNeeded但时间戳未设置WriteImpl()会返回InvalidArgument仅写 WAL 的场景如 write-committed 事务的 prepare 阶段则允许不设时间戳因为恢复时只有 commit marker 携带提交时间戳才会用到这些 keydb/db_impl/db_impl_write.cc。DBOptions 不兼容性检查不可变选项的组合冲突以下不可变的DBOptions冲突同样在运行时WriteImpl()中被检查db/db_impl/db_impl_write.cc条件原因two_write_queues enable_pipelined_write不兼容的写入模式unordered_write enable_pipelined_write不兼容的写入模式seq_per_batch enable_pipelined_write流水线写入不支持 seq_per_batch对应源码if (two_write_queues_ immutable_db_options_.enable_pipelined_write) { return Status::NotSupported( pipelined_writes is not compatible with concurrent prepares); } if (seq_per_batch_ immutable_db_options_.enable_pipelined_write) { return Status::NotSupported( pipelined_writes is not compatible with seq_per_batch); } if (immutable_db_options_.unordered_write immutable_db_options_.enable_pipelined_write) { return Status::NotSupported( pipelined_writes is not compatible with unordered_write); }此外pipelined 写入与post_memtable_callback、seq_per_batch与post_memtable_callback的组合也会被拒绝当前实现不保障这两个回调在对应模式下的语义。低优先级写入限流让低优写入在压缩压力下缓慢前进当WriteOptions::low_pri被设置且存在压缩压力WriteController::NeedSpeedupCompaction()返回 true时写入会在进入主写路径前通过WriteController::low_pri_rate_limiter()进行限流。这个限流器独立于主写入延迟机制其设计意图是即使压缩压力很大低优先级写入也能以慢速持续前进而不会彻底饿死。实现位于DBImpl::ThrottleLowPriWritesIfNeeded()db/db_impl/db_impl_write.ccif (write_controller_.NeedSpeedupCompaction()) { if (allow_2pc() (my_batch-HasCommit() || my_batch-HasRollback())) { // For 2PC, we only rate limit prepare, not commit. return Status::OK(); } if (write_options.no_slowdown) { return Status::Incomplete(Low priority write stall); } else { PERF_TIMER_FOR_WAIT_GUARD(write_delay_time); auto data_size my_batch-GetDataSize(); while (data_size 0) { size_t allowed write_controller_.low_pri_rate_limiter()-RequestToken( data_size, 0 /* alignment */, Env::IO_HIGH, nullptr /* stats */, RateLimiter::OpType::kWrite); data_size - allowed; } } }细节要点2PC 豁免对于两阶段提交2PCcommit 与 rollback 批次不参与低优先级限流以避免阻塞事务完成。源码注释明确For 2PC, we only rate limit prepare, not commit. 因为WriteBatch的HasCommit()/HasRollback()标志通过MarkCommit/MarkRollback设置可以直接判断批次类型。no_slowdown语义若同时设置了no_slowdown低优先级写入遇到压缩压力时直接返回Status::Incomplete(Low priority write stall)而不是阻塞等待调用方需要自行决定重试或丢弃。按字节限流限流以my_batch-GetDataSize()为总字节数循环向限流器RequestToken申请额度每次申请后从剩余字节中扣除直到全部额度取得。写路径分发四种写入模式的选择校验通过后WriteImpl()根据配置把写入分发到四种模式之一分发代码在 db/db_impl/db_impl_write.cc 附近Two-queue WAL-onlytwo_write_queues_ disable_memtable经由非 memtable 写线程路由到WriteImplWALOnly()。这是 WritePrepared 事务 prepare 批次等 WAL-only 写入的通道。Unordered writeunordered_write先通过WriteImplWALOnly()完成 WAL 写入再通过UnorderedWriteMemtable()独立并行插入 memtable牺牲顺序保证换取更高并发。Pipelined writeenable_pipelined_write路由到PipelinedWriteImpl()WAL 写入与 memtable 插入两个阶段在不同线程上重叠执行。Normal batched write默认单线程内按写组依次完成 WAL 追加与 memtable 插入顺序性最强。每种模式的完整语义、tradeoff 与实现细节参见 docs/components/write_flow/06_write_modes.md。从 docs/components/write_flow/index.md 可以了解到这些模式的共性特征无锁 leader 选举、三阶段自适应等待、组提交group commit将多个 writer 的 WAL 写入合并为一次 fsync以及 O(sqrt(n)) 两级唤醒的大写组并行 memtable 写入方案。小结与延伸阅读RocksDB 的写路径把用户视角的一次写入统一抽象为一个或多个 WriteBatch 记录再经由WriteImpl()的合法性校验、低优先级限流和模式分发最终落到 WAL 与 memtable。理解本文的 API 语义与 WriteBatch 编码是进一步研读写线程组提交、WAL 格式、序列号分配与崩溃恢复的前提。本系列其他章节位于 docs/components/write_flow/可以按需延伸阅读写线程与组提交02_write_thread.mdWAL 记录格式与生命周期03_wal.mdMemtable 插入与内部 key 编码04_memtable_insert.md序列号分配与可见性发布05_sequence_numbers.md四种写入模式详解06_write_modes.md流控与写入停顿07_flow_control.md墓碑生命周期08_tombstone_lifecycle.md崩溃恢复09_crash_recovery.md写路径性能10_performance.md关键源码路径汇总写路径主入口 db/db_impl/db_impl_write.cc、WriteBatch 类定义 include/rocksdb/write_batch.h、WriteBatch 序列化与内部工具 db/write_batch.cc、ValueType 枚举与内部 key db/dbformat.h、公开写入 API include/rocksdb/db.h。赞分享数据库KV存储嵌入式数据库存储【免费下载链接】rocksdbA library that provides an embeddable, persistent key-value store for fast storage.项目地址https://gitcode.com/gh_mirrors/ro/rocksdb点击查看免费下载相关推荐Windows界面终极定制ExplorerPatcher深度解析与专业级重构指南Windows界面终极定制ExplorerPatcher深度解析与专业级重构指南 ExplorerPatcher是一款专为Windows 11/10设计的开源桌面应用系统编程10倍性能差OceanBase批量写入 vs 单条写入实测从原理到调优全指南10倍性能差OceanBase批量写入 vs 单条写入实测从原理到调优全指南 你是否遇到过这样的困境业务高峰期单条写入延迟飙升至数百毫秒而批量导入却能轻数据库分布式数据库关系型数据库后端高可用LevelDB源码解析如何通过WriteBatch实现原子性操作的终极指南 LevelDB源码解析如何通过WriteBatch实现原子性操作的终极指南 在LevelDB数据库开发中确保数据一致性是至关重要的。想象一下当你需要人工智能AI 应用AI Agent创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表