强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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

参数详解

参数默认值说明
--benchmarksfillseq测试项(逗号分隔)
--num1000000记录数量
--value_size100Value 大小(字节)
--key_size16Key 大小(字节)
--db/tmp/leveldb_bench数据库路径
--cache_size8388608 (8MB)Block Cache 大小
--bloom_bits0Bloom Filter 位数(0=禁用)
--write_buffer_size4194304 (4MB)MemTable 大小
--max_open_files1000最大打开文件数
--compression1压缩(0=禁用,1=Snappy)
--threads1并发线程数
--histogram0输出延迟直方图
--sync0每次写入后 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
crc32cCRC32 校验性能
snappycompSnappy 压缩性能
snappyuncompSnappy 解压性能

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 占用高频繁 Compactiontop调整 Compaction 参数

11.9 本章小结

测试项关注指标典型值(NVMe SSD)
fillseq吞吐量~150 MB/s
fillrandom吞吐量~30 MB/s
readseq吞吐量~500 MB/s
readrandomops/s50K-200K ops/s
readrandom+Bloomops/s100K-500K ops/s

扩展阅读

  1. db_bench 源码benchmarks/db_bench.cc
  2. LevelDB 官方性能数据doc/benchmarks.md
  3. fio 工具:磁盘 I/O 基准测试
  4. perf 工具:CPU 性能分析

第 10 章 · Bloom Filter | 第 12 章 · 数据复制