1、YCSB Leveldb 测试
由于 leveldb 不提供 java 版本的接口,并且没有合适开源的 jni 转换代码,YCSB 无法直接使用
两种测试方式:
leveldb 本身没有服务端,没有 restful 接口,需要通过开源中间件进行交互:
simplehttp 和 simpleleveldb
目标数据库(比如 leveldb)需要作为服务端运行起来,并提供数据库操作的 Restful API 接口供调用:
1 2 3
| http://localhost:8080/put http://localhost:8080/get http://localhost:8080/del
|
膜拜大佬ing
1 2
| https://github.com/basicthinker/YCSB-C https://github.com/ls4154/YCSB-cpp
|
2、Rocksdb YCSB 测试原理
rocksdb 专门为 java 写了一版 jni 的转换方法,直接将 rocksdb 编译成 jni 并用 jar 打包,放到 ycsb lib 下进行调用即可
3、基于服务端中间件的 leveldb 测试步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| # get simplehttp git clone https://github.com/bitly/simplehttp.git
# get leveldb, install .a and .h to /usr/local git clone --recurse-submodules https://github.com/google/leveldb.git mkdir -p build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . sudo make install
# install dependency sudo apt install libjson-c-dev sudo apt install libsnappy-dev
打开 simplehttp/simpleleveldb/str_list_set.c修改 #include <json/json.h> 为 打开 simplehttp/simpleleveldb/simpleleveldb.c修改 #include <json/json.h> 为
simpleleveldb 的 Makefile -ljson应改为-ljson-c
|
simpleHttp 库制定 libevent1.4 作为依赖,去官网下载
1 2 3 4 5 6 7 8
| tar -zxvf libevent-release-1.4.15-stable.tar sudo apt-get install libtool cd libevent-release-1.4.15-stable ./autogen.sh ./configure make -j$(nproc) sudo make install sudo ln -s /usr/local/lib/libevent-1.4.so.2 /usr/lib/libevent-1.4.so.2
|
可能的错误:
1 2 3 4 5 6 7
| No Python installed ... ... regress.c:65:10: fatal error: regress.gen.h: No such file or directory
# 解决方案 vim ./event_rpcgen.py # 首行 python 改为 python2
|
安装依赖库:simplehttp,simpleleveldb
1 2 3 4 5 6 7
| cd simplehttp env LIBLEVELDB=/usr/local make sudo make install
cd simpleleveldb env LIBLEVELDB=/usr/local make sudo make install
|
可能的错误:
1 2 3 4
| cc -I. -I../simplehttp/.. -I/usr/local/include -I/usr/local/include -Wall -g -O2 -o simpleleveldb simpleleveldb.c str_list_set.c -L. -L../simplehttp -L/usr/local/lib -L/usr/local/lib -L/usr/local/lib -levent -ljson-c -lsimplehttp -lleveldb -lm -lstdc++ -lsnappy -lpthread /usr/bin/ld: /usr/local/lib/libsimplehttp.a(request.o):/home/zhdi/simplehttp/simplehttp/request.h:12: multiple definition of `simplehttp_reqs'; /usr/local/lib/libsimplehttp.a(simplehttp.o):/home/zhdi/simplehttp/simplehttp/request.h:12: first defined here collect2: error: ld returned 1 exit status make: *** [Makefile:26: simpleleveldb] Error 1
|
原因:
链接多次导致,试着把多声明的变成 extern 也许可以解决
本质原因还是 libsimplehttp.a 静态库有问题,实在不行找我,我这有编译没问题的静态库
服务端启动 leveldb
1
| simpleleveldb --address=localhost --port=8080 --db-file=./leveldb_file
|
检查是否成功
方案1:
打开浏览器,地址栏输入
http://localhost:8080/put?key=name&value=Niko

方案2:
1 2 3
| curl -X GET "http://localhost:8080/put?key=name&value=Niko"
# return: json status:OK
|
用 ycsb-leveldb.git 测试 leveldb(已放弃)
安装 YCSB:(有专门针对 leveldb 的)
1 2 3 4 5 6 7 8 9 10 11
| git clone https://github.com/jtsui/ycsb-leveldb.git cd ycsb-leveldb sudo apt install maven mvn -pl com.yahoo.ycsb:leveldb-binding -am clean package
# 配置 JAVA_HOME ~/.bashrc or ~/.zshrc export JAVA_HOME=/opt/jdk1.8.0_271 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH source ~/.bashrc
|
ycsb 负载
比如:random key-value pairs (8 B key, 1024 B value) are inserted repeatedly on a 90 GB dataset.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| # vim workload/workload90GB recordcount=96774193 operationcount=10000 workload=com.yahoo.ycsb.workloads.CoreWorkload
readallfields=true
readproportion=0 updateproportion=0 insertproportion=1 scanproportion=0
requestdistribution=zipfian fieldcount=1 fieldlength=1024 fieldlengthdistribution=constant
|
原始数据导入:
1 2
| cd ycsb_leveldb ./ycsb load leveldb -P workloads/workload90GB -s -p threadcount=16
|
数据运行:
1
| ./ycsb run leveldb -P workloads/workload90GB -s -p threadcount=16
|
4、CPU利用率分析
1 2 3 4 5 6
| sudo apt-get install sysstat mpstat -P ALL 1 # 每秒更新一次每个CPU核心的利用率
pidstat -p `pidof java` -u 1 500 # 每秒输出一次 CPU 使用统计,共输出 500 次 pidstat -p `pidof java` -r 1 500 # 每秒输出一次内存使用统计,共输出 500 次 pidstat -p `pidof java` -d 1 500 # 每秒输出一次 I/O 活动统计,共输出 500 次
|
测试 redis 性能
1 2 3 4 5 6 7
| git clone https://github.com/redis/redis.git make ./src/redis-server
cd ycsb ./bin/ycsb load redis -s -P workloads/workload90GB -p "redis.host=127.0.0.1" -p "redis.port=6379" ./bin/ycsb run redis -s -P workloads/workload90GB -p "redis.host=127.0.0.1" -p "redis.port=6379"
|
测试 rocksdb 性能
1 2
| ./bin/ycsb load rocksdb -s -P workloads/workload90GB -p rocksdb.dir=/tmp/ycsb-rocksdb-data ./bin/ycsb run rocksdb -s -P workloads/workload90GB -p rocksdb.dir=/tmp/ycsb-rocksdb-data
|
修改 rocksdb 源码后如何进行测试
以编译 rocksdb v7.6.0 为例
1 2 3
| git clone https://github.com/facebook/rocksdb.git cd rocksdb git checkout v7.6.0
|
更改makefile,压缩方式选 lz4 和 zstd 就行,其他三种特别是snappy对linux兼容不如mac
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| ifneq ($(ROCKSDB_JAVA_NO_COMPRESSION), 1) JAVA_COMPRESSIONS = liblz4.a libzstd.a endif
JAVA_STATIC_FLAGS = -DLZ4 -DZSTD JAVA_STATIC_INCLUDES = -I./lz4-$(LZ4_VER)/lib -I./zstd-$(ZSTD_VER)/lib -I./zstd-$(ZSTD_VER)/lib/dictBuilder
ifeq ($(JAVA_HOME),) $(error JAVA_HOME is not set) endif $(MAKE) clean-ext-libraries-bin $(MAKE) clean-rocks ARCHFLAG= $(MAKE) rocksdbjavastatic_deps ARCHFLAG= $(MAKE) rocksdbjavastatic_libobjects ARCHFLAG= ROCKSDBJNILIB="librocksdbjni-osx-$*.jnilib" $(MAKE) rocksdbjavastatic_javalib
rocksdbjavastaticrelease: rocksdbjavastaticosx rocksdbjava_javadocs_jar rocksdbjava_sources_jar cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class openssl sha1 java/target/$(ROCKSDB_JAR_ALL) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR_ALL).sha1
|
编译 jni(java native interface)
1 2 3 4 5 6 7 8
| # This will generate rocksdbjni.jar and librocksdbjni.so make -j$(nproc) rocksdbjava
# 检查 rocksdb/java/target/rocksdbjni-7.6.0-linux64.jar
# rocksdbjni.jar: 提供rocksdb java接口 # librocksdbjni.so: 提供C++的rocksdb库,并转换成java native接口供jar包调用
|
方法一:基于打包好的 ycsb-0.17.0.tar.gz
小缺点:无法自己修改 ycsb 源码
替换ycsb下的 rocksdb jni jar 包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| curl -O --location https://github.com/brianfrankcooper/YCSB/releases/download/0.17.0/ycsb-0.17.0.tar.gz tar xfvz ycsb-0.17.0.tar.gz cd ycsb-0.17.0
# 编译 rocksdb-binding java项目 mvn -pl site.ycsb:rocksdb-binding -am clean package
# 若要运行修改前的rocksdb版本 ./bin/ycsb load rocksdb -s -P workloads/workload10GB -p rocksdb.dir=/tmp/ycsb-rocksdb-data # 可以看到第一行:rocksdb的版本 5.11.3 vim /tmp/ycsb-rocksdb-data/LOG
# 替换jar包 cp rocksdb/java/target/rocksdbjni-7.6.0-linux64.jar ycsb-0.17.0/rocksdb-binding/lib rm rocksdbjni-5.11.3.jar rm -rf /tmp/ycsb-rocksdb-data
# 重新启动测试 ./bin/ycsb load rocksdb -s -P workloads/workload90GB -p rocksdb.dir=/tmp/ycsb-rocksdb-data ./bin/ycsb run rocksdb -s -P workloads/workload90GB -p rocksdb.dir=/tmp/ycsb-rocksdb-data
|
方法二:基于 YCSB.git
可以自由修改源码:
比如:
自定义每隔1秒输出吞吐:https://github.com/yanghy233/YCSB/commit/8b9a58cdddde3773445bd41b94f312d37da2a1e0
自定义 rocksdb jni 包在本地导入

1 2 3
| git clone https://github.com/yanghy233/YCSB.git git checkout 0.17.0-yhy mvn -pl site.ycsb:rocksdb-binding -am clean package
|
若要重新编译 YCSB rocksdb client
1 2
| # 不加 clean mvn -pl site.ycsb:rocksdb-binding -am package
|
基于C++实现的YCSB-C,专门用于测试CruiseDB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| sudo apt-get install libtbb-dev git clone git@github.com:yanghy233/YCSB-C.git cd YCSB-C
# 先到CruiseDB目录下编译 make static_lib -j$(nproc) make shared_lib -j$(nproc) sudo DEBUG_LEVEL=0 make uninstall sudo DEBUG_LEVEL=0 make install
# 链接与编译 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib make mkdir ramdisk_path rocksdb_disk_path
# 运行测试脚本 ./ycsba.sh
|
基于c++实现的 ycsb-cpp,可以在cpp层面直接测试leveldb
1 2 3 4 5 6 7 8 9 10
| git clone https://github.com/ls4154/YCSB-cpp.git
git submodule update --init mkdir build cd build # leveldb cmake -DBIND_LEVELDB=1 -DWITH_SNAPPY=1 -DWITH_LZ4=1 -DWITH_ZSTD=1 .. # rocksdb cmake -DBIND_ROCKSDB=1 -DWITH_SNAPPY=1 -DWITH_LZ4=1 -DWITH_ZSTD=1 .. make -j$(nproc)
|
Load & Run
1 2 3 4 5 6 7
| # leveldb ./build/ycsb -load -db leveldb -P workloads/workload10GB -P leveldb/leveldb.properties -s taskset -c 0 ./build/ycsb -run -db leveldb -P workloads/workload10GB -P leveldb/leveldb.properties -s
# rocksdb ./build/ycsb -load -db rocksdb -P workloads/workloadb -P rocksdb/rocksdb.properties -s ./build/ycsb -run -db rocksdb -P workloads/workloadb -P rocksdb/rocksdb.properties -s
|
修改leveldb源码后,测试前需要更新静态库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| sudo rm -rf /usr/local/include/leveldb /usr/local/lib64/cmake/leveldb /usr/local/lib64/libleveldb.a /usr/local/share/doc/leveldb \ /usr/local/lib/libleveldb.a \ /usr/local/lib/cmake/leveldb \ /usr/local/lib/cmake/leveldb/leveldbConfig.cmake \ /usr/local/lib/cmake/leveldb/leveldbConfigVersion.cmake \ /usr/local/lib/cmake/leveldb/leveldbTargets.cmake \ /usr/local/lib/cmake/leveldb/leveldbTargets-release.cmake
#
rm -rf /tmp/ycsb-leveldb
# cd leveldb/build cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . sudo make install
|
火焰图生成与分析
一些避坑记录:
- leveldb 一定需要 release 版本,否则会出现两个 pid
- perf 记录不能使用默认的 -g,要用 –call-graph dwarf,否则出现大量的 unknown 调用栈
- 无需绑定到单一核心
- 用 Centos8.2跑,ubuntu22有问题
1 2 3 4 5 6 7 8 9 10 11
| # perf 记录运行时 sudo perf record -F 999 -p `pidof ./build/ycsb` --call-graph dwarf -o ~/Experiment_Data/perf.data
# 以文本形式展开 sudo perf script -i ~/Experiment_Data/perf.data > ~/Experiment_Data/perf.unfold
# 生成脚本文件 ./FlameGraph/stackcollapse-perf.pl ~/Experiment_Data/perf.unfold > ~/Experiment_Data/perf.folded
# 生成火焰图 ./FlameGraph/flamegraph.pl ~/Experiment_Data/perf.folded > ~/Experiment_Data/flame.svg
|
leveldb 性能实验
配置:
硬件 |
Model |
CPU |
2核 Intel Xeon Processor (Cascadelake) |
Memory |
DDR4 16GB/ 32 GB |
Storage |
NVMe SSD 300GB |
软件 |
Version |
OS |
Ubuntu 22.04 LTS |
Kernel |
Linux 6.2 |
Leveldb |
Latest |
Rocksdb |
v7.6.0 |
YCSB Benchmark |
0.17.0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CPU:2核 Intel Xeon memory:16GB YCSB版本:https://github.com/yanghy233/YCSB-cpp
# loading 0.1GB rm -rf /tmp/ycsb-leveldb ./build/ycsb -load -db leveldb -P workloads/workload10GB -P leveldb/leveldb.properties -s
# running 50G # ycsb:默认-threads 1,绑定CPU核心0 taskset -c 0 ./build/ycsb -run -db leveldb -P workloads/workload10GB -P leveldb/leveldb.properties -s > ~/Experiment_Data/leveldb_ycsb.log
# 每秒记录一次CPU利用率 pidstat -p `pidof ./build/ycsb` -u 1 1400 > ~/Experiment_Data/leveldb_cpu.log
|
实验结果:
100% Insert 起始数据量 10GB
内存32G:吞吐量

我们的方案:

CPU 利用率

0830:
ours:

origin:

60% Insert, 40% Read 起始数据量 10GB
1 2 3 4 5 6 7 8 9 10 11
| cd YCSB-cpp
rm -rf /tmp/ycsb-leveldb ./build/ycsb -load -db leveldb -P workloads/workload10GB -P leveldb/leveldb.properties -s
# ops taskset -c 0 ./build/ycsb -run -db leveldb -P workloads/workload10GB -P leveldb/leveldb.properties -s > ~/Experiment_Data/leveldb_ycsb_6i4r.log
# cpu pidstat -p `pidof ./build/ycsb` -u 1 1400 > ~/Experiment_Data/leveldb_cpu_6i4r.log
|

workloads/workload10GB
0~300:6,121,420
300~600:4,698,732
600~900:3,784,508
900~1200:2,980,646
吞吐量

CPU 利用率

Rocksdb v7.6.0 性能实验
配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| CPU:2核 Intel Xeon memory:16GB YCSB版本:0.17.0 ./ycsb_0.17.0_yhy
# loading 10G rm -rf /tmp/ycsb-rocksdb-data ./bin/ycsb load rocksdb -s -P workloads/workload10GB -p rocksdb.dir=/tmp/ycsb-rocksdb-data
# running 50G # ycsb:默认-threads 1,绑定CPU核心0 taskset -c 0 ./bin/ycsb run rocksdb -s -P workloads/workload10GB -p rocksdb.dir=/tmp/ycsb-rocksdb-data -p measurementtype=timeseries > ~/Experiment_Data/rocksdb_ycsb.log
# 每秒记录一次CPU利用率 pidstat -p `pidof java` -u 1 1400 > ~/Experiment_Data/rocksdb_cpu.log
# 统一用 YCSB-cpp rm -rf /tmp/ycsb-rocksdb ./build/ycsb -load -db rocksdb -P workloads/workload10GB -P rocksdb/rocksdb.properties -s
# running ./build/ycsb -run -db rocksdb -P workloads/workload10GB -P rocksdb/rocksdb.properties -s > ~/Experiment_Data/rocksdb_ycsb.log
|
100% Insert
起始10G,insert 50G
实验结果:
吞吐量

前 800s 吞吐:


CruiseDB 性能测试
in YCSB-C,32c 32G
YCSB threadcount=100
默认 value = 1024B,
短时间内相对平稳:
(cruisedb_1024B.txt)


Rocksdb + Auto-tuned 测试:(能够提高总吞吐)
- 通过水位线窗口动态调控后台 flush / compaction IO数量,使后台 IO 数量稳定在一定的范围内
- 但无法解决前台 write stall 的问题

- CruiseDB 基础上,打开 Auto-tuned 机制:

总吞吐量(平均吞吐)的对比、稳定性(标准差)的对比
100%write

rocksdb_i1r0.txt

50%read,50%write


理想情况下的最大吞吐

CruiseDB 关于 L0 层是否移除
CruiseDB 最大层数与 Rocksdb 相同,都是最高在第 6 层
在 YCSB 负载执行期间,异步执行一个后台线程,每隔 5 秒输出各层的文件数量
分析作者有可能的实现:
- L0 文件数量限制为0,那么每次打印 L0 的数量都应该是 0;
- 逐层向上覆盖,原来的 L1 层是现在的 L0 层,则不应该出现 flush 操作;


其他
- kv 为 middle-size = 128B,吞吐量如下:

- kv 为 small-size = 32B,吞吐量如下:

CruiseDB冷启动问题发现与解答
冷启动原因:
- load 和 run 阶段分开,run才开始流量控制,导致开始时内存容量是满的
真实的 CruiseDB 负载吞吐(去除论文作者在 YCSB 刻意 sleep 的时间)
- 有 130s 的达到稳态的时间间隔(论文的图去掉了达到稳态的 130s 间隔,同时论文作者的YCSB刻意增加了130s的sleep)
- 下面实验图经过多次确认!

花了 65s 达到稳态

花了 40s 达到稳态

Reference
https://honor-ry.github.io/posts/de979122.html
libevent install:https://blog.csdn.net/u012342808/article/details/121482259