RocksDBunordered_write特性全解析放宽顺序保证以换取更高写入吞吐【免费下载链接】rocksdbA library that provides an embeddable, persistent key-value store for fast storage.项目地址: https://gitcode.com/gh_mirrors/ro/rocksdbunordered_write是 RocksDB 自 6.3 版本起提供的写路径优化选项通过放宽不可变快照等顺序保证显著提升高并发写入吞吐在与 TransactionDB 的 WritePrepared 事务模型组合时甚至可以在保持读自己写与不可变快照语义的同时获得 34%~42% 的吞吐提升。本文以官方博客文档为主体结合当前仓库中的选项定义与实现源码系统讲解该特性的设计动机、语义变化、两种配置方式、Benchmark 方法及其限制。背景RocksDB 默认提供的三大写路径保证在默认配置下RocksDB 的写路径向用户交付以下三组强保证原子读Atomic reads一个 WriteBatch 中的写入要么整体对读可见要么整体不可见不会出现读到半个批次的中间状态。读自己写Read-your-own-writes当某个写线程的Write调用返回后该线程随后的读操作一定能看到自己刚才写入的数据。不可变快照Immutable Snapshots读操作基于快照进行快照可见的数据集不会受任何在途in-flight或未来写入的影响是冻结的视图。这组保证由写线程队列、sequence number 分配与发布顺序等机制共同支撑代价是写路径上的串行化点与等待。对于写多读少、可容忍一定顺序松弛的应用这份代价并非总是必要——这正是unordered_write存在的意义。unordered_write放宽哪些保证保留哪些保证在 include/rocksdb/options.h 中unordered_write的注释对该特性做了权威定义默认值为false属于不可变选项需在打开 DB 前设置// Setting unordered_write to true trades higher write throughput with // relaxing the immutability guarantee of snapshots. ... // If set to true, although Read-Your-Own-Write property is still provided, // the snapshot immutability property is relaxed: the writes issued after the // snapshot is obtained (with larger sequence numbers) will be still not // visible to the reads from that snapshot, however, there still might be // pending writes (with lower sequence number) that will change the state // visible to the snapshot after they are landed to the memtable. // // Default: false bool unordered_write false;开启后语义变化如下保证默认 RocksDBunordered_write true读自己写Read-your-own-writes提供仍然提供原子读Atomic reads提供不再提供不可变快照Immutable Snapshots提供不再提供注释中特别说明了一个细节开启后快照取得之后才发布sequence number 更大的写入依然不会对快照可见但可能存在 sequence number 更小的在途写入它们在落地到 memtable 之后会改变该快照可见的状态——这就是不可变性被放宽的具体含义也解释了为什么::Get在快照上的可重复性repeatability以及Iterator、MultiGet的一致时间点视图会被破坏参见 include/rocksdb/db.h 中对该特性影响读 API 的说明。从底层实现看unordered_write的收益来源可以从 db/db_impl/db_impl_write.cc 与 db/db_impl/db_impl_write.cc 的注释窥见一斑该模式目前使用kDoPublishLastSeq的 sequence number 发布策略写入在 memtable 中以无序unordered方式落地从而减少写线程间的同步与等待。在 db/db_impl/db_impl.cc 附近也有开启 unordered_write 时key 以无序方式写入 memtable的对应注释。恢复不可变快照WritePrepared 事务 two_write_queues如果应用无法接受上述松弛语义官方给出的推荐方案是使用 TransactionDB并以WRITE_PREPARED写策略与two_write_queuestrue组合使用。在 include/rocksdb/options.h 中明确写道Using TransactionDB with WRITE_PREPARED write policy andtwo_write_queuestrueis one way to achieve immutable snapshots despite unordered_write.其中two_write_queues的定义include/rocksdb/options.h指出启用后写路径拆分为两个队列——一个用于disable_memtable只写 WAL的写入一个用于同时写 memtable 的写入避免 memtable 写入拖累其他写入这也正是 MySQL 2PC 场景只有串行的 commit 才写 memtable的优化基础。如何配置两种使用方式方式一保持默认三保证WritePrepared two_write_queues原文档给出的完整示例在保持原子读与不可变快照的同时获得吞吐提升DBOptions db_options; db_options.unordered_write true; db_options.two_write_queues true; DB* db; { TransactionDBOptions txn_db_options; txn_db_options.write_policy TxnDBWritePolicy::WRITE_PREPARED; txn_db_options.skip_concurrency_control true; TransactionDB* txn_db; TransactionDB::Open(options, txn_db_options, kDBPath, txn_db); db txn_db; } db-Write(...);其中write_policy与skip_concurrency_control均定义于 include/rocksdb/utilities/transaction_db.hwrite_policy数据落库策略默认为WRITE_COMMITTED只写已提交数据WRITE_PREPARED允许数据在 commit 阶段之前写入 DB由 DB 提供区分已提交与未提交数据的机制从而为无序写留出空间。skip_concurrency_control当 TransactionDB 仅用于写排序write ordering而非并发控制时可跳过内部锁管理该选项的设计意图即与DBOptions::unordered_write配合在仅以 TransactionDB 承担写排序职责时使用。方式二接受松弛保证普通 DB 直开如果应用可以自行处理更宽松的顺序语义则不需要 TransactionDB直接开启unordered_write即可且收益更大DBOptions db_options; db_options.unordered_write true; DB* db; DB::Open(db_options, kDBPath, db); db-Write(...);组合限制与校验源码里的硬性约束unordered_write并非可与所有选项随意组合当前仓库在打开 DB 与每次写入时都会做严格校验在 db/db_impl/db_impl_open.cc 中DBImpl::SanitizeOptions拒绝以下组合并返回Status::InvalidArgumentunordered_writetrue与allow_concurrent_memtable_writefalse不兼容无序写依赖并发 memtable 写入unordered_writetrue与enable_pipelined_writetrue不兼容。在 db/db_impl/db_impl_write.cc 中WriteImpl同样拒绝unordered_write与 pipelined write 的组合。在 db/db_impl/db_impl_write.cc 中IngestWriteBatchWithIndex通过WriteBatchWithIndex直接摄入不支持unordered_write会返回Status::NotSupported。配置时请务必避开上述组合否则 DB 打开或写入会直接报错。基准测试命令与结果原文档给出了db_bench基准测试命令核心参数含义如下TEST_TMPDIR/dev/shm/ ~/db_bench --benchmarksfillrandom --threads32 --num10000000 \ -max_write_buffer_number16 --max_background_jobs64 --batch_size8 \ --writes3000000 -level0_file_num_compaction_trigger99999 \ --level0_slowdown_writes_trigger99999 --level0_stop_writes_trigger99999 \ -enable_pipelined_writefalse -disable_auto_compactions \ --transaction_dbtrue --unordered_write1 --disable_wal0参数说明部分为原文档未展开、结合db_bench惯例补充--benchmarksfillrandom随机 key 顺序填充写入--threads3232 个并发写线程用于放大写路径竞争--num10000000/--writes3000000key 空间与总写入次数--batch_size8每批次写入 8 条记录-max_write_buffer_number16、--max_background_jobs64加大缓冲与后台任务配额避免 flush/compaction 成为瓶颈-level0_*_trigger99999将 L0 的 compaction 触发阈值抬到极大等效于压测期间基本不触发 L0 compaction-enable_pipelined_writefalse与unordered_write兼容的配置见上文校验-disable_auto_compactions关闭自动 compaction聚焦纯写入吞吐--transaction_dbtrue --unordered_write1开启 TransactionDB 与unordered_write--disable_wal0WAL 开启用于对比带 WAL 与不带 WAL--disable_wal1两种场景。原文档报告的结果相对于普通 RocksDB 的吞吐提升场景unordered_writetrue WritePrepared 事务unordered_writetrue松弛保证WAL 开启42%63%WAL 关闭No-WAL34%131%即保持三保证时提升 34%~42%接受松弛语义时提升可扩大至 63%~131%。需要注意这些数字来自官方博客发布时RocksDB 6.3 时代的特定软硬件与工作负载环境具体收益会随机器、工作负载与版本变化建议以本仓库版本重新运行db_bench实测为准。总结unordered_write的核心取舍非常清晰用不可变快照与原子读的语义换取写路径上更少的同步开销。它适合写多读少、对顺序一致性要求不高、或能够通过业务层补偿顺序问题的场景若必须保持原语义则需叠加two_write_queuestrue与 WritePrepared 事务write_policyWRITE_PREPARED、skip_concurrency_controltrue。配置时注意其与enable_pipelined_write、allow_concurrent_memtable_writefalse及 WriteBatch 直接摄入等选项的硬性不兼容约束。相关选项定义可继续查阅 include/rocksdb/options.h 与 include/rocksdb/utilities/transaction_db.h底层写路径实现可参考 db/db_impl/db_impl_write.cc。【免费下载链接】rocksdbA library that provides an embeddable, persistent key-value store for fast storage.项目地址: https://gitcode.com/gh_mirrors/ro/rocksdb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考