LevelDB 完全指南 / 第 11 章 · 性能基准测试
第 11 章 · 性能基准测试
11.1 db_bench 简介
db_bench 是 LevelDB 官方提供的性能基准测试工具,随源码一起编译。它支持多种测试场景,是评估和调优 LevelDB 性能的标准工具。
位置:build/db_bench
编译时需要启用:
cmake -DLEVELDB_BUILD_BENCHMARKS=ON ..
11.2 基本用法
常用命令
# 填充 100 万条记录
./db_bench --benchmarks=fillseq --num=1000000 --value_size=100
# 随机写入测试
./db_bench --benchmarks=fillrandom --num=1000000 --value_size=100
# 随机读取测试
./db_bench --benchmarks=readrandom --num=1000000
# 顺序读取测试
./db_bench --benchmarks=readseq --num=1000000
# 覆盖写测试
./db_bench --benchmarks=overwrite --num=1000000
# 综合测试
./db_bench --benchmarks="fillseq,readseq,fillrandom,readrandom" \
--num=1000000 --value_size=100
参数详解
| 参数 |
默认值 |
说明 |
--benchmarks |
fillseq |
测试项(逗号分隔) |
--num |
1000000 |
记录数量 |
--value_size |
100 |
Value 大小(字节) |
--key_size |
16 |
Key 大小(字节) |
--db |
/tmp/leveldb_bench |
数据库路径 |
--cache_size |
8388608 (8MB) |
Block Cache 大小 |
--bloom_bits |
0 |
Bloom Filter 位数(0=禁用) |
--write_buffer_size |
4194304 (4MB) |
MemTable 大小 |
--max_open_files |
1000 |
最大打开文件数 |
--compression |
1 |
压缩(0=禁用,1=Snappy) |
--threads |
1 |
并发线程数 |
--histogram |
0 |
输出延迟直方图 |
--sync |
0 |
每次写入后 fsync |
11.3 支持的测试项
写入测试
| 测试项 |
说明 |
fillseq |
顺序 Key 填充 |
fillrandom |
随机 Key 填充 |
fill100K |
填充 100KB 的 Value |
overwrite |
覆盖已有 Key |
fillsync |
同步写入(每次 fsync) |
fillbatch |
批量写入 |
读取测试
| 测试项 |
说明 |
readseq |
顺序扫描 |
readreverse |
反向扫描 |
readrandom |
随机点读 |
readmissing |
读取不存在的 Key |
readhot |
读取热点 Key(1% 的 Key 被 90% 的请求访问) |
readwhilewriting |
边读边写 |
混合测试
| 测试项 |
说明 |
readrandomwriterandom |
随机读写混合 |
readwhilescanning |
点读和扫描混合 |
compact |
手动触发 Compaction |
crc32c |
CRC32 校验性能 |
snappycomp |
Snappy 压缩性能 |
snappyuncomp |
Snappy 解压性能 |
11.4 典型测试场景
场景一:顺序写入 vs 随机写入
# 顺序写入
./db_bench --benchmarks=fillseq --num=1000000 \
--value_size=100 --db=/tmp/bench_seq
# 随机写入
./db_bench --benchmarks=fillrandom --num=1000000 \
--value_size=100 --db=/tmp/bench_random
典型结果(NVMe SSD):
| 测试项 |
吞吐量 |
延迟 (avg) |
| fillseq |
~150 MB/s |
~0.6 μs |
| fillrandom |
~30 MB/s |
~3 μs |
场景二:读取性能对比
# 先填充数据
./db_bench --benchmarks=fillrandom --num=1000000 \
--value_size=100 --db=/tmp/bench_read
# 顺序读取
./db_bench --benchmarks=readseq --num=1000000 \
--db=/tmp/bench_read --use_existing_db=1
# 随机读取
./db_bench --benchmarks=readrandom --num=1000000 \
--db=/tmp/bench_read --use_existing_db=1
# 随机读取(带 Bloom Filter)
./db_bench --benchmarks=readrandom --num=1000000 \
--db=/tmp/bench_read --use_existing_db=1 --bloom_bits=10
典型结果:
| 测试项 |
吞吐量 |
延迟 (avg) |
| readseq |
~500 MB/s |
~0.2 μs |
| readrandom (无 Bloom) |
~50,000 ops/s |
~20 μs |
| readrandom (有 Bloom) |
~200,000 ops/s |
~5 μs |
场景三:缓存影响
# 小缓存
./db_bench --benchmarks=readrandom --num=1000000 \
--cache_size=8388608 --db=/tmp/bench_smallcache
# 大缓存
./db_bench --benchmarks=readrandom --num=1000000 \
--cache_size=536870912 --db=/tmp/bench_largecache
场景四:压缩影响
# 无压缩
./db_bench --benchmarks="fillseq,readseq" --num=1000000 \
--compression=0 --db=/tmp/bench_nocomp
# Snappy 压缩
./db_bench --benchmarks="fillseq,readseq" --num=1000000 \
--compression=1 --db=/tmp/bench_snappy
11.5 性能指标解读
db_bench 输出示例
fillrandom : 3.453 micros/op; 28.5 MB/s
readrandom : 5.672 micros/op; 17.2 MB/s
readseq : 0.213 micros/op; 462.1 MB/s
关键指标
| 指标 |
说明 |
关注点 |
| micros/op |
每次操作的平均延迟 |
越低越好 |
| MB/s |
吞吐量 |
越高越好 |
| ops/s |
每秒操作数 |
等于 1,000,000 / micros/op |
| P50/P99/P99.9 |
延迟百分位数 |
关注尾部延迟 |
延迟直方图
./db_bench --benchmarks=readrandom --num=1000000 --histogram=1
# 输出类似:
# Percentiles:
# P50: 3.2 us
# P99: 12.8 us
# P99.9: 45.6 us
11.6 自定义基准测试代码
#include <chrono>
#include <iostream>
#include <random>
#include <vector>
#include "leveldb/db.h"
#include "leveldb/filter_policy.h"
#include "leveldb/statistics.h"
class Benchmark {
public:
Benchmark(const std::string& db_path,
size_t cache_size = 128 * 1024 * 1024)
: db_path_(db_path) {
leveldb::Options opts;
opts.create_if_missing = true;
opts.write_buffer_size = 64 * 1024 * 1024;
opts.max_open_files = 500;
opts.filter_policy = leveldb::NewBloomFilterPolicy(10);
opts.block_cache = leveldb::NewLRUCache(cache_size);
opts.statistics = leveldb::CreateDBStatistics();
stats_ = opts.statistics;
leveldb::Status s = leveldb::DB::Open(opts, db_path_, &db_);
if (!s.ok()) throw std::runtime_error(s.ToString());
}
~Benchmark() {
delete db_;
delete stats_;
}
// 顺序写入测试
void BenchFillSeq(int num, int value_size) {
std::string value(value_size, 'x');
auto start = Now();
for (int i = 0; i < num; i++) {
char key[32];
snprintf(key, sizeof(key), "%016d", i);
db_->Put(leveldb::WriteOptions(), key, value);
}
Report("fillseq", num, start);
}
// 随机写入测试
void BenchFillRandom(int num, int value_size) {
std::string value(value_size, 'x');
std::mt19937 rng(42);
auto start = Now();
for (int i = 0; i < num; i++) {
char key[32];
snprintf(key, sizeof(key), "%016d", rng());
db_->Put(leveldb::WriteOptions(), key, value);
}
Report("fillrandom", num, start);
}
// 随机读取测试
void BenchReadRandom(int num) {
std::mt19937 rng(42);
std::string value;
auto start = Now();
int found = 0;
for (int i = 0; i < num; i++) {
char key[32];
snprintf(key, sizeof(key), "%016d", rng());
if (db_->Get(leveldb::ReadOptions(), key, &value).ok()) {
found++;
}
}
Report("readrandom", num, start);
std::cout << " Found: " << found << "/" << num << std::endl;
}
// 顺序读取测试
void BenchReadSeq(int num) {
auto start = Now();
auto* it = db_->NewIterator(leveldb::ReadOptions());
int count = 0;
for (it->SeekToFirst(); it->Valid(); it->Next()) {
count++;
}
delete it;
Report("readseq", count, start);
}
// 批量写入测试
void BenchWriteBatch(int num, int batch_size, int value_size) {
std::string value(value_size, 'x');
auto start = Now();
for (int i = 0; i < num; i += batch_size) {
leveldb::WriteBatch batch;
int end = std::min(i + batch_size, num);
for (int j = i; j < end; j++) {
char key[32];
snprintf(key, sizeof(key), "%016d", j);
batch.Put(key, value);
}
db_->Write(leveldb::WriteOptions(), &batch);
}
std::string label = "writebatch_" + std::to_string(batch_size);
Report(label, num, start);
}
// 打印统计信息
void PrintStats() {
std::cout << "\n=== Statistics ===" << std::endl;
std::cout << stats_->ToString() << std::endl;
}
private:
using TimePoint = std::chrono::high_resolution_clock::time_point;
TimePoint Now() {
return std::chrono::high_resolution_clock::now();
}
void Report(const std::string& name, int num, TimePoint start) {
auto end = Now();
auto us = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
double ops_per_sec = (double)num / us.count() * 1e6;
double us_per_op = (double)us.count() / num;
std::cout << name << ": "
<< us_per_op << " us/op; "
<< ops_per_sec << " ops/s" << std::endl;
}
leveldb::DB* db_;
leveldb::Statistics* stats_;
std::string db_path_;
};
int main() {
Benchmark bench("/tmp/custom_bench", 256 * 1024 * 1024);
int num = 1000000;
int value_size = 100;
std::cout << "=== 写入测试 ===" << std::endl;
bench.BenchFillSeq(num, value_size);
bench.BenchFillRandom(num, value_size);
std::cout << "\n=== 读取测试 ===" << std::endl;
bench.BenchReadRandom(num);
bench.BenchReadSeq(num);
std::cout << "\n=== 批量写入测试 ===" << std::endl;
bench.BenchWriteBatch(num, 100, value_size);
bench.BenchWriteBatch(num, 1000, value_size);
bench.PrintStats();
return 0;
}
11.7 调优实验
实验一:write_buffer_size 对写入性能的影响
for size in 4194304 16777216 67108864 268435456; do
echo "=== write_buffer_size = $size ==="
./db_bench --benchmarks=fillrandom --num=5000000 \
--write_buffer_size=$size --db=/tmp/bench_wb$size
done
实验二:block_cache 对读取性能的影响
for cache in 8388608 67108864 268435456 1073741824; do
echo "=== block_cache = $cache ==="
./db_bench --benchmarks=readrandom --num=1000000 \
--cache_size=$cache --use_existing_db=1 \
--db=/tmp/bench_cache$cache
done
实验三:Bloom Filter 效果
echo "=== 无 Bloom Filter ==="
./db_bench --benchmarks=readrandom --num=1000000 \
--bloom_bits=0 --db=/tmp/bench_bloom0
echo "=== Bloom Filter 10 bits ==="
./db_bench --benchmarks=readrandom --num=1000000 \
--bloom_bits=10 --db=/tmp/bench_bloom10
echo "=== Bloom Filter 14 bits ==="
./db_bench --benchmarks=readrandom --num=1000000 \
--bloom_bits=14 --db=/tmp/bench_bloom14
11.8 常见性能问题诊断
| 症状 |
可能原因 |
诊断方法 |
解决方案 |
| 写入突然变慢 |
L0 Compaction 堆积 |
检查 LOG |
增大 write_buffer_size |
| 读取延迟波动 |
Compaction I/O 争用 |
iostat 监控 |
使用 SSD |
| 内存使用过高 |
Block Cache 过大 |
top / ps |
减小 cache_size |
| 磁盘空间增长 |
Tombstone 未清理 |
du -sh |
手动 CompactRange |
| CPU 占用高 |
频繁 Compaction |
top |
调整 Compaction 参数 |
11.9 本章小结
| 测试项 |
关注指标 |
典型值(NVMe SSD) |
| fillseq |
吞吐量 |
~150 MB/s |
| fillrandom |
吞吐量 |
~30 MB/s |
| readseq |
吞吐量 |
~500 MB/s |
| readrandom |
ops/s |
50K-200K ops/s |
| readrandom+Bloom |
ops/s |
100K-500K ops/s |
扩展阅读
- db_bench 源码:
benchmarks/db_bench.cc
- LevelDB 官方性能数据:
doc/benchmarks.md
- fio 工具:磁盘 I/O 基准测试
- perf 工具:CPU 性能分析
← 第 10 章 · Bloom Filter | 第 12 章 · 数据复制 →