RocksDB Windows 移植全解析:从微软 Bing 团队的移植实践看平台抽象层设计

发布时间:2026/9/19 17:24:36
RocksDB Windows 移植全解析:从微软 Bing 团队的移植实践看平台抽象层设计 RocksDB Windows 移植全解析从微软 Bing 团队的移植实践看平台抽象层设计【免费下载链接】rocksdbA library that provides an embeddable, persistent key-value store for fast storage.项目地址: https://gitcode.com/gh_mirrors/ro/rocksdb导读RocksDB 是一个面向快速存储优化的嵌入式持久化键值存储库其设计目标是随 CPU 数量和存储 IOPS 线性扩展以支撑 IO 密集型、内存型与写一次write-once等多样化工作负载。本文以仓库根目录下的 WINDOWS_PORT.md 为核心主体系统讲解微软 Bing 团队将 RocksDB 移植到 Windows 平台时所做的决策与改动从 CMake 构建系统的选型、port/port.h平台抽象层对 POSIX 语义的复刻到线程本地存储、jemalloc 集成的底层实现并完整呈现当时的性能基准数据。读完本文你将理解 RocksDB 平台移植层的整体架构、Windows 环境下文件 I/O 与线程模型的实现思路以及如何在 Windows 上构建和运行 RocksDB。移植背景为什么是微软 Bing 团队来做RocksDB 作为一个成熟的开源持久化键值存储库被广泛用于对扩展性要求极高的业务场景。微软 Bing 团队长期致力于提升平台的扩展性与效率因此选择拥抱开源将 RocksDB 引入其技术栈进行使用、增强和定制并同步将成果回馈给 RocksDB 社区。本文档正是这一 Windows 移植工作的配套说明记录了移植过程中的关键决策供审查者与其他 Windows 用户参考。文档列出了四位主要贡献者Alexander Zinoviev、Dmitri Smirnov、Praveen Rao 与 Sherlock Huang。所有移植、测试和基准测试工作均基于Windows Server 2012 R2 Datacenter 64-bit完成从使用的 API 来看文档作者认为移植成果同样适用于 Vista 之后的其他 Windows 版本。移植目标零分叉、最小改动、全部测试通过移植工作设定了明确的约束目标这些目标直接决定了后文所述的技术选型复用 RocksDB 已有的移植接口即port/port.h抽象层而非为 Windows 另起炉灶对平台无关代码做最小化修改只在必要处用条件编译等方式适配系统差异所有单元测试在 Debug 与 Release 构建下全部通过文档特别注明当时 SyncPoint 的引入似乎导致db_test无法在 Release 下运行性能与已发布的基准相当考虑硬件差异保持移植代码与主干内联、不做长期分叉no forking。这些目标中最关键的是第一点——make use of the existing porting interface。RocksDB 的平台抽象层位于 port/port.h它定义了线程、互斥锁、条件变量、文件系统等与操作系统相关的接口POSIX 与 Windows 各自提供实现。Windows 移植正是围绕这一层展开的。构建系统基于 CMake 的 64 位构建移植团队选择了CMake作为 Windows 构建系统理由是它被广泛接受、构建速度快且便捷同时生成的 Visual Studio 工程既可以命令行使用也可以从 IDE 使用。当时的计划还提到希望将现有的 make 构建系统与新的 cmake 构建系统合并实现全平台统一构建。当前仓库的顶层 CMakeLists.txt 仍然保留了完整的 Windows 构建说明其头部注释给出了 Windows 构建的完整步骤注意当前版本的 CMake 构建仅支持 64 位文档也明确指出未对 32 位做测试早期报告显示 32 位无法运行# Prerequisites: # 你必须至少安装 Visual Studio 2019并在其附带的 Developer Command Prompt 中执行构建 # 同时保证 git.exe 位于 %PATH% 环境变量中。 # 1. 在 thirdparty.inc 文件中更新第三方库的路径 # 2. 创建构建产物目录 # mkdir build # cd build # 3. 运行 cmake 生成 Windows 工程文件可附加选项启用所需第三方库 # cmake -G Visual Studio 16 2019 -DCMAKE_BUILD_TYPERelease # -DWITH_GFLAGS1 -DWITH_SNAPPY1 -DWITH_JEMALLOC1 -DWITH_JNI1 .. # 4. Debug 模式构建可用 /m[:N] 指定 msbuild 并行线程数/m 表示使用全部核心 # msbuild rocksdb.sln # 注意rocksdb.sln 在 Release 模式下会排除仅用于测试的代码 # 若构建 ALL_BUILDRelease 模式下测试专用代码不会被编译。 # 5. Release 模式构建 # msbuild rocksdb.sln /p:ConfigurationRelease文档中提到的另一个构建相关文件 thirdparty.inc 同样位于仓库顶层需要编辑以指向实际第三方库的安装位置。整体构建流程可以用下表概括步骤操作说明1编辑thirdparty.inc指向第三方库gflags、snappy、jemalloc 等实际路径2mkdir build cd build创建并进入构建目录3cmake -G Visual Studio 16 2019 -D... ..生成 VS 工程按需开启WITH_GFLAGS、WITH_SNAPPY、WITH_JEMALLOC、WITH_JNI等选项4msbuild rocksdb.slnDebug 构建默认可用/m并行加速5msbuild rocksdb.sln /p:ConfigurationReleaseRelease 构建平台抽象层C 与 STL 兼容处理移植过程中团队对平台无关代码做了最小化改动主要分为两类一类是操作系统差异另一类是当时 MSVC 编译器对 C11 支持不完善带来的工作区。当前仓库的 port/port_posix.h 与 port/win/port_win.h 正是这一工作的载体。具体处理手法包括POSIX 头文件替换Windows 上没有且不必要的头文件如unistd.h用#ifndef OS_WIN隔离POSIX 专用头文件统一替换为port/port.hdirent.h替换为 port/port_dirent.h在rocksdb::port命名空间内实现相应接口sys/time.h替换为 port/sys_time.h。格式化占位符Windows 不支持printf %z说明符。为此定义了字符串宏ROCKSDB_PRIsztPOSIX 下展开为zu见 port/port_posix.hWindows 下展开为Iu见 port/win/port_win.h。该宏在仓库中被广泛使用例如 cache/sharded_cache.cc、db/compaction/compaction.cc 等日志与统计输出场景。常量与 constexpr当时编译器对constexpr支持不完整std::numeric_limits::max/min()被替换为 C 宏常量个别场景需要把类成员改为static const并在.cc文件中给出定义函数级constexpr在一处被替换为模板特化。语言兼容性部分类内成员初始化被移入构造函数非平凡构造函数的 union 成员在一处被替换为char[]同时修复了 spatial 实验性特性中的 bug零长度数组被视为非标准扩展被转换为 1 元素数组。时间与初始化当时std::chrono缺少纳秒支持在后续 STL 版本中修复因此 port/win/env_win.cc 中使用QueryPerformanceCounter()实现高精度计时函数局部静态变量的初始化在当时并不安全WinEnv使用std::once来规避。在 port/win/port_win.h 中可以看到这些抽象在 Windows 侧的具体落地Mutex基于std::mutex实现并支持调试断言RWMutex基于 Windows SRW LockAcquireSRWLockShared/AcquireSRWLockExclusive等CondVar基于std::condition_variable同时通过#undef min/max/DeleteFile/GetCurrentTime清理 Windows 头文件的宏污染。Windows 环境Env实现逐项复刻 posix_env移植团队的目标是让 Windows 环境与posix_env功能对齐即尽可能精确地复刻线程池及其他功能包括用std::thread原语复刻 POSIX 逻辑实现 posix_env 的全部磁盘访问功能对WinWritableFile与WinRandomAccessFile设置use_os_bufferfalse以禁用 OS 磁盘缓冲用带OVERLAPPED结构的WriteFile/ReadFile替代pread/pwrite用SetFileInformationByHandle弥补fallocate的缺失。当前仓库的 port/win/env_win.h 展示了这一架构WinEnvThreads持有std::vectorThreadPoolImpl管理各优先级后台线程池并支持ReserveThreads/ReleaseThreads/SetBackgroundThreads等动态调优接口WinFileSystem实现FileSystem接口覆盖顺序读、随机读、可写文件、随机读写文件、内存映射文件、目录操作、文件锁、硬链接等全部磁盘访问能力WinClock实现SystemClock提供微秒/纳秒级时间戳。线程池不依赖 Windows 自带实现虽然 Windows 提供了自己的高效线程池实现但移植团队选择用std::thread原语复刻 posix 逻辑。这样做的理由是任何人可以快速发现 posix 源码中的变更并在 windows env 中同步复刻——这一做法在实践中被证明非常有效。同时想要替换内置线程池的用户可以借助 RocksDB 的**可叠加环境stackable environments**机制自行定制。磁盘访问OVERLAPPED 替代 pread/pwriteWindows 没有pread/pwrite这样的原子定位读写系统调用。移植采用带OVERLAPPED结构的WriteFile/ReadFile来模拟OVERLAPPED的Offset/OffsetHigh字段直接指定本次磁盘操作的位置从而实现原子定位 同步执行的语义。从 port/win/io_win.cc 的实现可以看到pwrite构造OVERLAPPED后调用WriteFilepread同理调用ReadFile并对ERROR_HANDLE_EOF等边界情况做了处理。与 POSIX 的唯一差异是文件指针不会恢复到原位置——考虑到随机访问的特性这几乎无影响。无缓冲 I/Ouse_os_buffer 的 Windows 语义use_os_buffer标志在 POSIX 平台上表示通过fadvise机制禁用读前推日志read-ahead。Windows 没有fadvise系统调用且其磁盘缓存实现与 Linux 差异很大——Windows 上通过**无缓冲磁盘访问un-buffered disk access**来控制内存占用是常见做法。因此移植团队将use_os_bufferfalse定义为对WinWritableFile与WinRandomAccessFile禁用 OS 磁盘缓冲。代价是磁盘吞吐量下降可通过增大配置的内存缓存来补偿而在无缓冲访问不合理的场景如 WAL 与 MANIFEST 文件下选项为 true 时类以标准方式工作。无缓冲模式还受操作系统对齐约束磁盘偏移、缓冲区地址、单次读写数据量都需满足扇区对齐要求——port/win/io_win.cc 中的IsSectorAligned正是用于检查偏移是否按扇区大小对齐。fallocate 与 truncateSetFileInformationByHandleWindows 没有fallocate。移植团队用SetFileInformationByHandle实现两项能力见 port/win/io_win.cc通过FileAllocationInfo快速预分配磁盘空间以加速 I/O通过FileEndOfFileInfo在写满最后一页后截断文件。文档特别指出与 Linux 的两点差异预分配的空间不会像 Linux 那样用零填充但好处是预分配后文件的 EOF 位置不会被修改。文件并发访问放宽权限RocksDB 会在文件仍被其他句柄打开时执行重命名、复制和删除操作。为此 Windows 移植放宽了几乎所有并发访问权限允许这类操作在文件被占用的情形下进行。线程本地存储Thread-Local Storage线程本地存储对 RocksDB 的性能至关重要。移植团队没有另写一套实现而是在rocksdb::port命名空间内创建内联包装器将pthread_specific系列调用转发到 Windows 的Tls接口。在 port/win/port_win.h 中可以清晰看到这种映射pthread_key_create对应TlsAllocpthread_key_delete对应TlsFreepthread_setspecific对应TlsSetValuepthread_getspecific对应TlsGetValue。这样保留了原有逻辑主体不变可维护性也得到保证。Windows 的 TLS 原语不支持线程退出时的自动清理而 RocksDB 的线程池会复用线程必须确保每个线程退出时释放 TLS 数据。为此在 util/thread_local.cc 中加入了少量 Windows 专用代码把清理回调注入到.CRT$XLB数据段中的__tls结构。具体做法是定义一个PIMAGE_TLS_CALLBACK类型的常量p_thread_callback_on_exit用#pragma const_seg(.CRT$XLB)放入对应段并通过#pragma comment(linker, /INCLUDE:__tls_used)强制链接器保留该符号。回调WinOnThreadExit在DLL_THREAD_DETACH事件中调用TlsGetValue获取线程本地指针并执行清理。该机制保证了无论 RocksDB 被用于可执行文件、独立 DLL 还是被其他 DLL 加载清理回调都会被系统加载器调用。Jemalloc 集成初始化顺序与 new/delete 重定向当 RocksDB 与 jemalloc 配合使用时jemalloc 必须在任何 C 全局对象或静态对象初始化之前完成初始化。为此移植团队向.CRT$XCT段注入初始化例程由运行时在初始化静态对象前自动调用同时将je-uninit排入atexit()确保退出时对称地反初始化。jemalloc 对全局new/delete操作符的重定向由链接器完成前提是满足文档构建章节所列条件。当前仓库的 port/win/win_jemalloc.cc 正是这一机制的载体它只应在启用了ROCKSDB_JEMALLOC的构建中参与编译否则直接报错其中重载了全局operator new、operator new[]、operator delete、operator delete[]全部转发到je_malloc/je_free分配失败时抛出std::bad_alloc同时提供jemalloc_aligned_alloc/jemalloc_aligned_free供 port/win/port_win.h 中的cacheline_aligned_alloc使用并顺带为 ZSTD 提供JemallocAllocateForZSTD分配回调。有意未实现的功能Stack Trace 与未处理异常处理器移植团队决定不实现栈回溯Stack Trace与未处理异常处理器Unhandled Exception Handler两个特性理由是宿主程序通常已自带这两项能力。调试时借助调试器或分析进程转储process dump即可因此未将其列为优先事项。这一取舍在 port/win/port_win.h 中亦有体现——例如Crash/ImmediateExit在 Windows 侧仅提供最简实现。性能测试结果性能基准在同一组机器上运行测试环境如下2 × Intel(R) Xeon(R) E5 2450 2.10 GHz共 16 核2 × XK0480GDQPH SSD共 894GB 可用磁盘128 GB 内存Windows Server 2012 R2 Datacenter1 亿个 key每个 key 10 字节每个 value 800 字节数据库总大小约 76GB结果基于 RocksDB 3.11部分对比 3.10除特别说明外参数与官方 Wiki 页面发布的基准完全一致说明以下数据为文档作者在 2015 年前后的硬件与 RocksDB 3.x 版本上实测所得仅代表当时的移植成果不能直接外推为当前版本或现代硬件的性能结论。Flash 存储基准测试 1随机顺序批量加载Bulk Load of Keys in Random Order版本总运行时间FillrandomCompact3.1117.6 min5.480 micros/op182465 ops/sec142.0 MB/s486056544.000 micros/op3.1016.2 min5.018 micros/op199269 ops/sec155.1 MB/s441313173.000 micros/op测试 2顺序批量加载Bulk Load of Keys in Sequential Order版本Fillseq3.114.944 micros/op约 202k ops/sec157.4 MB/s3.104.105 micros/op243.6k ops/sec189.6 MB/s测试 3随机写Random Write启用无缓冲 I/O版本Overwrite3.1152.661 micros/op18.9k ops/sec14.8 MB/s3.1052.661 micros/op18.9k ops/sec测试 4随机读Random Read启用无缓冲 I/O版本Readrandom3.1115.716 micros/op63.6k ops/sec49.5 MB/s3.1015.548 micros/op64.3k ops/sec测试 5多线程读 单线程写Readwhilewriting启用无缓冲 I/O版本Readwhilewriting3.1125.128 micros/op39.7k ops/sec3.1024.854 micros/op40.2k ops/sec内存型基准测试 1点查Point Lookup80K writes/sec 写入速率下版本实际写入速率Readwhilewriting3.1140.5k write/sec0.314 micros/op3187455 ops/sec364.8 MB/s715454999/715454999 命中3.1050.6k write/sec0.316 micros/op3162028 ops/sec719576999/719576999 命中10K writes/sec 写入速率下版本实际写入速率Readwhilewriting3.115.8k write/sec0.246 micros/op4062669 ops/sec464.9 MB/s915481999/915481999 命中3.105.8k write/sec0.244 micros/op4106253 ops/sec927986999/927986999 命中测试 2前缀范围查询Prefix Range Query80K writes/sec 写入速率下版本实际写入速率Readwhilewriting3.1146.3k write/sec0.362 micros/op2765052 ops/sec316.4 MB/s611549999/611549999 命中3.1045.8k write/sec0.317 micros/op3154941 ops/sec708158999/708158999 命中10K writes/sec 写入速率下版本实际写入速率Readwhilewriting3.115.78k write/sec0.269 micros/op3716692 ops/sec425.3 MB/s837401999/837401999 命中3.105.7k write/sec0.261 micros/op3830152 ops/sec863482999/863482999 命中文档作者认为性能仍有很大提升空间并计划持续投入优化。总结与启发从 WINDOWS_PORT.md 这份微软贡献文档中可以提炼出 RocksDB Windows 移植的核心方法论善用既有抽象层所有平台差异都被收敛到port/目录下的实现中平台无关代码几乎不受影响这保证了移植代码能与主干长期保持内联、不产生分叉语义对齐优先无论是用OVERLAPPED模拟pread/pwrite、用SetFileInformationByHandle模拟fallocate还是用std::thread复刻 POSIX 线程池逻辑目标都是让 Windows 环境在语义上与posix_env对等尊重平台特性针对 Windows 磁盘缓存与 Linux 的差异将use_os_bufferfalse重新定义为无缓冲 I/O 并配合扇区对齐约束展现了移植而非照搬的态度务实取舍TLS 清理、jemalloc 初始化顺序这类硬骨头用 CRT 段注入解决而栈回溯、异常处理器这类宿主程序已覆盖的能力则明确放弃。对于需要在 Windows 上使用或二次开发 RocksDB 的读者建议从 port/win/ 目录的env_win.h、io_win.cc、port_win.h三个文件入手它们分别对应环境抽象、文件 I/O 与基础类型兼容三层构建与第三方库配置则参考 CMakeLists.txt 头部注释与 thirdparty.inc。【免费下载链接】rocksdbA library that provides an embeddable, persistent key-value store for fast storage.项目地址: https://gitcode.com/gh_mirrors/ro/rocksdb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询