C++从零开始实现LSM-Tree-KV存储-13-性能测试

前段时间折腾毕业论文去了, 好久没更新这个系列了, 今天继续…

本小节我们来测试一下我们的KV存储的性能, 我们使用Redis官方提供的redis-benchmark工具来测试性能

代码仓库:ToniXWD/toni-lsm: A KV storage engine based on LSM Tree, supporting Redis RESP

欢迎点个Star

1 redis-benchmark 介绍

redis-benchmark 是 Redis 官方提供的一款性能测试工具,主要用于评估 Redis 服务器在不同操作下的性能表现。它能够模拟多个客户端同时发送请求,帮助开发者了解系统在高并发场景下的响应时间和吞吐量。该工具支持多种命令测试,并且可以自定义测试参数,如连接数、请求数、数据大小等。通过这些测试结果,我们可以对 LSM-Tree-KV 存储的性能进行对比和优化。

安装redis时,默认安装的redis-benchmark工具,安装路径为/usr/bin/redis-benchmark,我们可以直接使用redis-benchmark命令来测试性能。

1
2
3
4
# 1. 在一个 Terminal 开启 redis-server
redis-server
# 2. 在另一个 Terminal 中运行 redis-benchmark
redis-benchmark -h 127.0.0.1 -p 6379

常见使用方法

1
2
3
4
5
6
7
8
# 基础测试:100个并发客户端,10万次请求,仅显示每秒请求数
redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000 -q

# 指定测试命令(SET/GET)和随机键范围
redis-benchmark -t set,get -r 100000 -n 1000000

# 使用管道化请求提升吞吐量(16条命令合并发送)
redis-benchmark -n 1000000 -t set,get -P 16 -q

项目测试对比数据
由于我们基于LSM Tree实现的Redis兼容层只支持部分的API, 因此我们可以通过-t来指定测试的内容, 和我们的Redis兼容层进行对比, 我们可以得到以下测试结果:

1
2
3
4
5
6
7
(base) ➜  ~ redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000 -q -t SET,GET,INCR,SADD,HSET,ZADD
SET: 102850.12 requests per second, p50=0.527 msec
GET: 121926.12 requests per second, p50=0.527 msec
INCR: 120252.45 requests per second, p50=0.511 msec
SADD: 109474.31 requests per second, p50=0.503 msec
HSET: 132857.14 requests per second, p50=0.527 msec
ZADD: 117388.53 requests per second, p50=0.551 msec

2 本项目兼容redis-benchmark

如果按照我们目前的实现来测试, 是会报错的, 因为我们缺少对Ping/Pong的处理函数, 我们简单修改代码支持就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
std::string handleRequest(const std::string &request) {
size_t pos = 0;

if (request.empty()) {
return "-ERR Protocol error: expected '*'\r\n";
}

if (request == "PING\r\n") {
return "+PONG\r\n";
}
...
}

接下来我们运行自己的兼容redisserver进行测试:

1
2
3
4
# 1. 在一个 Terminal 开启 我们自己实现的server
(base) ➜ toni-lsm git:(master) ✗ xmake run server
# 2. 在另一个 Terminal 中运行 redis-benchmark
(base) ➜ ~ redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000 -q -t SET,GET,INCR,SADD,HSET,ZADD

得到结果如下:

1
2
3
4
5
6
7
WARNING: Could not fetch server CONFIG
SET: 142653.36 requests per second, p50=0.527 msec
GET: 134589.50 requests per second, p50=0.503 msec
INCR: 132802.12 requests per second, p50=0.503 msec
SADD: 131233.59 requests per second, p50=0.519 msec
HSET: 123456.79 requests per second, p50=0.583 msec
ZADD: 126422.25 requests per second, p50=0.615 msec

可以看到, 我们实现的server对这几个常见的接口和redis官方的redis-server是在一个数量级的, 有的接口甚至性能更优

但这里没有放 list 的测试结果, 因为我们的list使用字符串拼接的方式实现, 事实上证明这个设计很垃圾, 后续要优化, QPS 没眼看就不放上来了

3 优化分析

我们目前的实现还是存在一些性能缺陷的, 其中我们回顾我们的代码发现存在如下问题:

  1. 我们的MemTable并发控制有缺陷, 用的是一把大锁, 应该同时为活跃表和冻结表设置锁, 增大并发量
  2. 我们的SST可以添加布隆过滤器, 来拦截一些无效的查询, 这对减少文件IO非常有用

下一章我们将实现这些优化