首页
/ LevelDB 深度解析:Google 键值存储库的特性、CMake 构建指南、性能报告与头文件导航

LevelDB 深度解析:Google 键值存储库的特性、CMake 构建指南、性能报告与头文件导航

2026-09-05 15:24:40作者:庞队千Virginia

LevelDB 是 Google 开发的快速键值(Key-Value)存储库,提供从字符串键到字符串值的有序映射。本文基于官方 README 展开,覆盖其核心特性、明确的使用边界、POSIX 与 Windows 下的 CMake 构建流程、随附 db_bench 基准测试的性能数据解读,并逐头文件介绍 include/leveldb/ 公共接口——读完后你将能够独立完成该库的编译、链接、基本读写与性能评估。

项目定位与维护现状

LevelDB 的核心定位是“可靠且快速的键值存储”:数据按键排序持久化,键和值都允许是任意字节数组,排序顺序可以通过自定义比较器覆盖。项目的作者是 Sanjay Ghemawat 和 Jeff Dean(见 README.md)。

需要特别注意的一点是:官方在 README 开头明确声明,该仓库目前处于非常有限的维护状态,只评审两类变更:

  • 关键 Bug 的修复,例如数据丢失或内存破坏;
  • 内部支持的 LevelDB 客户端绝对必需的变更(通常是修复语言/标准库/OS 更新引入的破坏)。

这一声明对使用者的影响是:公共 API(include/leveldb/*.h)会长期保持稳定,但也意味着不建议依赖仓库对新兴操作系统或构建系统的快速支持。

核心特性:逐条对应到源码

README 列出的九项特性,均可以在本仓库的公共头文件中找到对应实现:

特性 源码依据
键值均为任意字节数组,按 key 排序存储 db.hDB 被定义为“persistent ordered map from keys to values”
可自定义比较器覆盖排序 comparator.h;默认比较器为按字节字典序,构造函数中绑定于 util/options.ccBytewiseComparator()
基础操作 Put / Get / Delete db.h 三个纯虚函数
多变更原子批处理 write_batch.h,批内更新按添加顺序原子应用
瞬时快照获得一致视图 db.hSnapshot,不可变、可跨线程无同步访问;通过 DB::GetSnapshot() / ReleaseSnapshot() 管理
支持正向与反向迭代 iterator.hSeekToFirst / SeekToLast / Next / Prev
默认 Snappy 压缩,也支持 Zstd options.hCompressionTypekNoCompression / kSnappyCompression / kZstdCompression),CMake 会探测系统是否具备 snappy、zstd 库(CMakeLists.txt
外部活动(文件系统操作等)经虚拟接口转发 env.h 抽象 OS 环境;POSIX 实现在 util/env_posix.cc,Windows 实现在 util/env_windows.cc

两个值得在源码层面注意的细节:

  1. 压缩是块粒度的options.h 注释说明用户数据被组织成一组 block,每个 block 在落盘前可被单独压缩;CompressionType 的枚举值属于磁盘持久格式的一部分,不能改动(options.h)。Zstd 压缩级别由 zstd_compression_level 控制,当前支持范围是 [-5, 22],默认值为 1。
  2. WriteBatch 强调顺序语义。头文件注释给出了经典例子:依次 Put("key","v1")Delete("key")Put("key","v2")Put("key","v3") 后,最终值为 "v3"write_batch.h)。跨 key 的原子移动场景下,“先 Delete 后 Put”的顺序也是避免数据丢失的关键。

使用边界:README 明确声明的三条限制

README 的 Limitations 一节划清了 LevelDB 的能力边界,这三条在选型时必须牢记:

  1. 这不是 SQL 数据库——没有关系数据模型、不支持 SQL 查询、没有索引支持;
  2. 同一时刻只允许单进程(可以多线程)访问一个数据库——这一点与 db.h 的注释一致:DB 支持多线程并发访问且无需外部同步,但进程间并发访问不提供保障;
  3. 库内建没有 client-server 支持——需要网络访问的应用必须自行在库之上封装服务端。

换言之,LevelDB 适合作为嵌入式存储引擎(单机、本地、键值语义),而不是分布式数据服务。

获取源码与构建

获取源码

README 给出的获取方式(需要拉取子模块,因为测试与基准依赖 third_party/googletestthird_party/benchmark):

git clone --recurse-submodules https://gitcode.com/GitHub_Trending/leveldb4/leveldb.git

构建前提

CMakeLists.txt 可以确认当前仓库的实际构建要求:

  • 项目版本为 1.23.0CMakeLists.txtproject(leveldb VERSION 1.23.0 LANGUAGES C CXX)),与 db.hkMajorVersion = 1kMinorVersion = 23 保持同步;
  • CMake 最低 3.22CMakeLists.txt);
  • C++17 必需,C 标准使用 C11 并允许优雅退化到 C89(CMakeLists.txt);
  • 编译过程默认禁用 C++ 异常与 RTTI(-fno-exceptions -fno-rtti 或 MSVC 的 /EHs-c- /GR-),因此使用方代码也应按无异常模式编写;
  • CMake 会自动探测可选依赖:crc32csnappyzstdtcmalloc,找到则链接(CMakeLists.txtCMakeLists.txt)。系统安装 snappy/zstd 后,压缩能力才会生效。

POSIX 平台(Linux / macOS)快速构建

README 给出的快速开始命令:

mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .

默认开启的三个构建开关(均为 ON,见 CMakeLists.txt):

  • LEVELDB_BUILD_TESTS:构建基于 GoogleTest 的单元测试(leveldb_tests 及若干独立测试可执行文件,注册到 CTest);
  • LEVELDB_BUILD_BENCHMARKS:构建 db_bench 等基准程序;
  • LEVELDB_INSTALLmake install 时安装头文件、库及 CMake 包配置(cmake/leveldbConfig.cmake.in 生成的 leveldb:: 命名空间目标)。

如果只需要库本身,可以用 -DLEVELDB_BUILD_TESTS=OFF -DLEVELDB_BUILD_BENCHMARKS=OFF 缩短构建时间。

Windows 平台构建

README 给出的流程(先生成 Visual Studio 2017 工程文件):

mkdir build
cd build
cmake -G "Visual Studio 15" ..

默认生成 x86 工程;需要 64 位时使用:

cmake -G "Visual Studio 15 Win64" ..

从命令行编译解决方案:

devenv /build Debug leveldb.sln

或直接打开 leveldb.sln 在 Visual Studio 内构建。README 同时提示:更高级的用法参见 CMake 官方文档与仓库内的 CMakeLists.txt

构建产物

leveldb 库外,构建还会产出:

  • leveldbutil 命令行工具(源文件 db/leveldbutil.cc),用于 dump/compact 数据库,是排查线上数据的常用手段;
  • 测试与基准可执行文件,如 benchmarks/db_bench.cc 对应的 db_bench,以及 SQLite3 / Kyoto Cabinet 对比基准(在检测到对应库时才编译,见 CMakeLists.txt)。

性能报告:来自随附 db_bench 的实测数据

README 附带了一份由 db_bench 程序生成的性能报告。需要注意适用前提:数据来自 LevelDB 1.1(2011 年)4 x Intel Core 2 Quad Q6600 @ 2.40GHz(4MB 缓存)机器上的运行结果,官方原文即说明“结果有一定噪声,只够给出量级估计”。数据库规模:100 万条记录,每条 16 字节 key + 100 字节 value(压缩后约 50 字节),原始数据约 110.6 MB,压缩落盘约 62.9 MB。

写入性能

fill 系列基准创建全新数据库(顺序或随机 key),fillsync 在每次操作后把数据从操作系统刷到磁盘,其余写操作留在 OS 缓冲缓存中;overwrite 对已有 key 做随机更新。

fillseq      :       1.765 micros/op;   62.7 MB/s
fillsync     :     268.409 micros/op;    0.4 MB/s (10000 ops)
fillrandom   :       2.460 micros/op;   45.0 MB/s
overwrite    :       2.380 micros/op;   46.5 MB/s

每个 op 对应一次单键值对写入,即随机写约 40 万次/秒。README 还特别解释了 fillsync 为什么比一次磁盘寻道(典型 10ms)便宜得多(0.3ms):怀疑是硬盘自身用内存缓存了这次更新并在数据真正写到盘片前就返回了确认,其安全性取决于硬盘掉电时能否保住自己的缓存——这解释了为什么 WriteOptions::sync 的语义与 write() + fsync() 相当,但实际时延仍远低于一次机械寻道。

读取性能

该基准数据库较小,工作集可完全放入内存,因此数据刻画的是“工作集在内存中”的读取表现;工作集超出 OS 缓冲缓存后,读取成本将由 1~2 次磁盘寻道主导,而写入性能基本不受影响。

# 大量随机写之后、compaction 之前
readrandom  : 16.677 micros/op;  (approximately 60,000 reads per second)
readseq     :  0.476 micros/op;  232.3 MB/s
readreverse :  0.724 micros/op;  152.9 MB/s

# 后台 compaction 之后(通常由 LevelDB 自动触发)
readrandom  : 11.602 micros/op;  (approximately 85,000 reads per second)
readseq     :  0.423 micros/op;  261.8 MB/s
readreverse :  0.663 micros/op;  166.9 MB/s

再叠加一块足够大的 block cache,让解压后的数据块驻留内存,随机读还会进一步提升:

readrandom  : 9.775 micros/op;  (approximately 100,000 reads per second before compaction)
readrandom  : 5.215 micros/op;  (approximately 190,000 reads per second after compaction)

三组数据的递进关系说明了两个调优杠杆:compaction 收敛数据block cache 消除重复解压。在 benchmarks/db_bench.cc 中可以看到默认的基准清单正是在 fillseq / fillsync / fillrandom / ... 之后额外插入一轮 readrandom 以“等待先前 compaction 静默”,与上述“compaction 后更好”的结论相互印证。若想在本机复现,编译后运行 db_bench 并按 README 说明调整参数即可(注意硬件差异,数据只作量级参考)。

仓库结构与公共头文件指南

README 指引读者参阅 doc/index.md(用法详解)与 doc/impl.md(实现概览),并给出了一条重要约定:公共接口全部位于 include/leveldb/*.h,调用方不应包含或依赖包内任何其他头文件的细节,这些内部 API 可能随时变更

公共头文件逐一说明(继承自 README,并结合源码补充):

  • include/leveldb/db.h:DB 的主接口,从这里开始。核心 API 包括 DB::OpenPut / Delete / Write / GetNewIteratorGetSnapshot / ReleaseSnapshotGetProperty(可查询 leveldb.statsleveldb.sstablesleveldb.num-files-at-level<N> 等属性)、GetApproximateSizesCompactRangedb->CompactRange(nullptr, nullptr) 压缩整个库)。文件级工具函数 DestroyDBRepairDB(损坏时尽力抢救数据)也在此定义;

  • include/leveldb/options.h:控制整个数据库行为的 Options,以及控制单次读写的 ReadOptions / WriteOptions。关键默认值一览:

    选项 默认值 说明
    comparator 字节字典序 同一 DB 多次 Open 必须使用同名同序的比较器
    create_if_missing false 库缺失时是否创建
    error_if_exists false 库已存在时是否报错
    paranoid_checks false 激进的数据校验,发现损坏提前停止
    write_buffer_size 4 MB memtable 切换阈值;最多两个写缓冲驻留内存,调大提升批量加载性能但拉长恢复时间
    max_open_files 1000 工作集大时按“每 2MB 一个文件”预算调大
    block_cache nullptr(内部自动创建 8MB cache) 非空则使用指定 cache
    block_size 4 KB 未压缩数据按 block 打包,运行时可动态修改
    block_restart_interval 16 key 差量编码的 restart 间隔
    max_file_size 2 MB 单 SSTable 目标大小;调大可减少文件数但拉长 compaction
    compression kSnappyCompression Snappy 轻量快速,多数场景不必换成不压缩
    zstd_compression_level 1 仅对 Zstd 生效,范围 [-5, 22]
    reuse_logs false 实验性:复用已有 MANIFEST 与 log 以加速 Open
    filter_policy nullptr 建议传入 NewBloomFilterPolicy() 减少磁盘读

    ReadOptions 提供 verify_checksums(默认关)、fill_cache(默认开,全量扫描时可关)、snapshot 三个字段;WriteOptions 只有 sync(默认 false)——其语义在头文件注释中写得很清楚:sync==falsewrite() 系统调用同级的崩溃语义,sync==true 等价于 write() + fsync()options.h);

  • include/leveldb/comparator.h:用户自定义比较器抽象。只想要字节序就用默认比较器;需要自定义顺序(如处理不同字符编码)可自行实现;

  • include/leveldb/iterator.h:迭代器接口,从 DB::NewIterator() 获得;初始状态无效,必须先调用某个 Seek 方法;支持 Valid / Seek / SeekToFirst / SeekToLast / Next / Prev

  • include/leveldb/write_batch.h:原子应用多个更新的接口,另提供 Append 合并批、ApproximateSize 估算批大小、Iterate 遍历批内操作;

  • include/leveldb/slice.h:指向其他字节数组的“指针 + 长度”轻量封装,是贯穿整个 API 的基本类型;

  • include/leveldb/status.h:多数公共接口返回的 Status,用于报告成功与各类错误,典型用法 if (!s.ok()) cerr << s.ToString() << endl;

  • include/leveldb/env.h:OS 环境抽象(文件读写、后台任务调度等),POSIX 实现在 util/env_posix.cc;自定义文件系统交互(如内存文件系统)时从这里入手,仓库自带内存版实现 helpers/memenv/memenv.cc

  • include/leveldb/table.hinclude/leveldb/table_builder.h:底层模块,大多数客户端不会直接使用。

文档目录还有两块与本文主题直接相关的材料:doc/table_format.md 描述 SSTable 的持久格式,doc/log_format.md 描述 WAL 日志格式。doc/impl.md 则概述了数据库在目录中的文件组织——*.log(追加的近期更新,配合 memtable 服务读)与 *.ldb 排序表(按 level 组织、通过 compaction 逐级下沉)、CURRENT 指向最新 MANIFEST——理解这些是正确使用 max_file_sizewrite_buffer_size 等参数的前提。

贡献代码:四条硬性要求与 PR 流程

README 的 Contributing 一节给出了明确门槛,任何贡献需同时满足:

  1. 只接受已测试平台——POSIX(Linux、macOS)或 Windows;其他平台的变更只作为极小改动的例外;

  2. 稳定 API——迫使 LevelDB 使用方修改代码的变更,若无充分收益可能被拒绝;

  3. 必须带测试——所有变更需附带新增(或修改)的测试,或给出充分理由;

  4. 统一风格——遵循 Google C++ 风格指南,提交前对文件执行:

    clang-format -i --style=file <file>
    

PR 流程上:作者须先签署 Google 的 CLA;为保持提交时间线线性(便于与 Google 内部仓库同步),需要将变更 squash 为单个 commit 并 rebase 到 main 分支。README 还特别说明:构建配置文件(如 CMakeLists.txt)的贡献通常不会被接受,项目专注维护少数受支持的构建配置。

小结

LevelDB 提供了一套克制而完整的嵌入式 KV 存储方案:有序键值、自定义比较器、原子批、快照、双向迭代、Snappy/Zstd 压缩与可插拔的 Env 抽象,配合文档化清晰的 Options 默认值;其边界(非 SQL、单进程访问、无内建服务化)同样被官方明确划出。构建层面,仓库以 CMake 3.22 + C++17 为基线,POSIX 与 Windows 各有官方验证过的命令序列;性能层面,随附的 db_bench 报告虽然源自 2011 年的硬件,但“compaction 与 block cache 决定随机读表现”“sync 写时延受硬盘缓存影响”等结论对今天的调优依然有效。建议以 doc/index.md 的 API 讲解为起点、以 include/leveldb/db.h 为第一份精读的源码,逐步深入到 doc/impl.md 描述的 LSM 式存储结构。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384