第 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 章 · 数据复制 →