RocksDB Windows 移植全解析:从微软 Bing 团队的移植实践看平台抽象层设计
2026/9/19 17:24:28 网站建设 项目流程

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_TYPE=Release # -DWITH_GFLAGS=1 -DWITH_SNAPPY=1 -DWITH_JEMALLOC=1 -DWITH_JNI=1 .. # 4. Debug 模式构建(可用 /m[:<N>] 指定 msbuild 并行线程数,/m 表示使用全部核心) # msbuild rocksdb.sln # 注意:rocksdb.sln 在 Release 模式下会排除仅用于测试的代码; # 若构建 ALL_BUILD,Release 模式下测试专用代码不会被编译。 # 5. Release 模式构建 # msbuild rocksdb.sln /p:Configuration=Release

文档中提到的另一个构建相关文件 thirdparty.inc 同样位于仓库顶层,需要编辑以指向实际第三方库的安装位置。整体构建流程可以用下表概括:

步骤操作说明
1编辑thirdparty.inc指向第三方库(gflags、snappy、jemalloc 等)实际路径
2mkdir build && cd build创建并进入构建目录
3cmake -G "Visual Studio 16 2019" -D... ..生成 VS 工程,按需开启WITH_GFLAGSWITH_SNAPPYWITH_JEMALLOCWITH_JNI等选项
4msbuild rocksdb.slnDebug 构建(默认),可用/m并行加速
5msbuild rocksdb.sln /p:Configuration=ReleaseRelease 构建

平台抽象层:C++ 与 STL 兼容处理

移植过程中,团队对平台无关代码做了"最小化"改动,主要分为两类:一类是操作系统差异,另一类是当时 MSVC 编译器对 C++11 支持不完善带来的工作区。当前仓库的 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_PRIszt:POSIX 下展开为"zu"(见 port/port_posix.h),Windows 下展开为"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 Lock(AcquireSRWLockShared/AcquireSRWLockExclusive等);CondVar基于std::condition_variable;同时通过#undef min/max/DeleteFile/GetCurrentTime清理 Windows 头文件的宏污染。

Windows 环境(Env)实现:逐项复刻 posix_env

移植团队的目标是让 Windows 环境与posix_env功能对齐,即尽可能精确地复刻线程池及其他功能,包括:用std::thread原语复刻 POSIX 逻辑;实现 posix_env 的全部磁盘访问功能;对WinWritableFileWinRandomAccessFile设置use_os_buffer=false以禁用 OS 磁盘缓冲;用带OVERLAPPED结构的WriteFile/ReadFile替代pread/pwrite;用SetFileInformationByHandle弥补fallocate的缺失。

当前仓库的 port/win/env_win.h 展示了这一架构:WinEnvThreads持有std::vector<ThreadPoolImpl>管理各优先级后台线程池,并支持ReserveThreads/ReleaseThreads/SetBackgroundThreads等动态调优接口;WinFileSystem实现FileSystem接口,覆盖顺序读、随机读、可写文件、随机读写文件、内存映射文件、目录操作、文件锁、硬链接等全部磁盘访问能力;WinClock实现SystemClock,提供微秒/纳秒级时间戳。

线程池:不依赖 Windows 自带实现

虽然 Windows 提供了自己的高效线程池实现,但移植团队选择用std::thread原语复刻 posix 逻辑。这样做的理由是:任何人可以快速发现 posix 源码中的变更,并在 windows env 中同步复刻——这一做法在实践中被证明非常有效。同时,想要替换内置线程池的用户,可以借助 RocksDB 的**可叠加环境(stackable environments)**机制自行定制。

磁盘访问:OVERLAPPED 替代 pread/pwrite

Windows 没有pread/pwrite这样的原子定位读写系统调用。移植采用带OVERLAPPED结构的WriteFile/ReadFile来模拟:OVERLAPPEDOffset/OffsetHigh字段直接指定本次磁盘操作的位置,从而实现"原子定位 + 同步执行"的语义。从 port/win/io_win.cc 的实现可以看到,pwrite构造OVERLAPPED后调用WriteFilepread同理调用ReadFile,并对ERROR_HANDLE_EOF等边界情况做了处理。与 POSIX 的唯一差异是文件指针不会恢复到原位置——考虑到随机访问的特性,这几乎无影响。

无缓冲 I/O:use_os_buffer 的 Windows 语义

use_os_buffer标志在 POSIX 平台上表示通过fadvise机制禁用读前推日志(read-ahead)。Windows 没有fadvise系统调用,且其磁盘缓存实现与 Linux 差异很大——Windows 上通过**无缓冲磁盘访问(un-buffered disk access)**来控制内存占用是常见做法。因此移植团队将use_os_buffer=false定义为:对WinWritableFileWinRandomAccessFile禁用 OS 磁盘缓冲。代价是磁盘吞吐量下降,可通过增大配置的内存缓存来补偿;而在无缓冲访问不合理的场景(如 WAL 与 MANIFEST 文件)下,选项为 true 时类以标准方式工作。无缓冲模式还受操作系统对齐约束:磁盘偏移、缓冲区地址、单次读写数据量都需满足扇区对齐要求——port/win/io_win.cc 中的IsSectorAligned正是用于检查偏移是否按扇区大小对齐。

fallocate 与 truncate:SetFileInformationByHandle

Windows 没有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")强制链接器保留该符号。回调WinOnThreadExitDLL_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 newoperator new[]operator deleteoperator 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 Datacenter
  • 1 亿个 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)

版本总运行时间FillrandomCompact
3.1117.6 min5.480 micros/op,182465 ops/sec,142.0 MB/s486056544.000 micros/op
3.1016.2 min5.018 micros/op,199269 ops/sec,155.1 MB/s441313173.000 micros/op

测试 2:顺序批量加载(Bulk Load of Keys in Sequential Order)

版本Fillseq
3.114.944 micros/op,约 202k ops/sec,157.4 MB/s
3.104.105 micros/op,243.6k ops/sec,189.6 MB/s

测试 3:随机写(Random Write,启用无缓冲 I/O)

版本Overwrite
3.1152.661 micros/op,18.9k ops/sec,14.8 MB/s
3.1052.661 micros/op,18.9k ops/sec

测试 4:随机读(Random Read,启用无缓冲 I/O)

版本Readrandom
3.1115.716 micros/op,63.6k ops/sec,49.5 MB/s
3.1015.548 micros/op,64.3k ops/sec

测试 5:多线程读 + 单线程写(Readwhilewriting,启用无缓冲 I/O)

版本Readwhilewriting
3.1125.128 micros/op,39.7k ops/sec
3.1024.854 micros/op,40.2k ops/sec

内存型基准

测试 1:点查(Point Lookup)

80K writes/sec 写入速率下:

版本实际写入速率Readwhilewriting
3.1140.5k write/sec0.314 micros/op,3187455 ops/sec,364.8 MB/s(715454999/715454999 命中)
3.1050.6k write/sec0.316 micros/op,3162028 ops/sec(719576999/719576999 命中)

10K writes/sec 写入速率下:

版本实际写入速率Readwhilewriting
3.115.8k write/sec0.246 micros/op,4062669 ops/sec,464.9 MB/s(915481999/915481999 命中)
3.105.8k write/sec0.244 micros/op,4106253 ops/sec(927986999/927986999 命中)

测试 2:前缀范围查询(Prefix Range Query)

80K writes/sec 写入速率下:

版本实际写入速率Readwhilewriting
3.1146.3k write/sec0.362 micros/op,2765052 ops/sec,316.4 MB/s(611549999/611549999 命中)
3.1045.8k write/sec0.317 micros/op,3154941 ops/sec(708158999/708158999 命中)

10K writes/sec 写入速率下:

版本实际写入速率Readwhilewriting
3.115.78k write/sec0.269 micros/op,3716692 ops/sec,425.3 MB/s(837401999/837401999 命中)
3.105.7k write/sec0.261 micros/op,3830152 ops/sec(863482999/863482999 命中)

文档作者认为性能仍有很大提升空间,并计划持续投入优化。

总结与启发

从 WINDOWS_PORT.md 这份微软贡献文档中,可以提炼出 RocksDB Windows 移植的核心方法论:

  1. 善用既有抽象层:所有平台差异都被收敛到port/目录下的实现中,平台无关代码几乎不受影响,这保证了移植代码能与主干长期保持内联、不产生分叉;
  2. 语义对齐优先:无论是用OVERLAPPED模拟pread/pwrite、用SetFileInformationByHandle模拟fallocate,还是用std::thread复刻 POSIX 线程池逻辑,目标都是让 Windows 环境在语义上与posix_env对等;
  3. 尊重平台特性:针对 Windows 磁盘缓存与 Linux 的差异,将use_os_buffer=false重新定义为无缓冲 I/O 并配合扇区对齐约束,展现了"移植而非照搬"的态度;
  4. 务实取舍:TLS 清理、jemalloc 初始化顺序这类硬骨头用 CRT 段注入解决,而栈回溯、异常处理器这类宿主程序已覆盖的能力则明确放弃。

对于需要在 Windows 上使用或二次开发 RocksDB 的读者,建议从 port/win/ 目录的env_win.hio_win.ccport_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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询