fctsdb-bench
fctsdb场景性能测试工具基于开源的influxdb-comparisons工具重构而来。 用于实现海东青性能测试用例设计)的内容.
influxdb-comparisons工具数据生成、写入、查询等所有过程分开,每个过程一套工具。 fctsdb-bench设计时不采用这种**,一个fctsdb-bench工具集合了数据的生成、写入、查询语句生成、查询等所有命令
1 编译
进入fctsdb-bench的目录,在此目录下执行以下命令make即可生成fctsdb-bench工具
会生成三个可执行文件:
fcbench是本机可以执行程序,一般使用这个程序
fcbench-amd64是amd64处理器的linux系统可执行文件
fcbench-arm64是arm64处理器的linux系统可执行文件
2 工具使用手册
使用fcbench -h可以查看所有支持的子命令,如下:
Available Commands:
agent 代理程序,和数据库运行在一起,支持被远程调用开启关闭数据库(开发团队内部使用)
list 展示所有场景(case)和对应的查询语句类型(query-type)
mixed 混合读写测试
mock 模仿海东青数据库,测试本工具能力上限
query 生成查询语句并直接发送至数据库
schedule 从配置文件中读取执行任务并顺序执行
write 生成数据并直接发送至数据库
query-gen(隐藏命令) 生成数据库查询语句,输出到stdout,搭配query-load使用
query-load(隐藏命令) 从文件或者stdin载入查询语句,并发送查询到数据库,需要先使用query-gen命令
data-gen(隐藏命令) 生成不同场景(case)的数据,输出到stdout,搭配data-load使用
data-load(隐藏命令) 从文件或者stdin载入数据,并发送数据到数据库,需要先使用data-gen命令
2.1 数据写入测试
使用fcbench write命令可以进行数据写入测试 -h查看帮助信息,支持的flags如下:
Flags:
--urls string *被测数据库的地址 (default "http://localhost:8086")
--db string *数据库的database名称 (default "benchmark_db")
--use-case string *使用的测试场景(可选场景: vehicle, air-quality, devops) (default "vehicle")
--scale-var int *场景的变量,一般情况下是场景中模拟机的数量 (default 1)
--scale-var-offset int *场景偏移量,一般情况下是模拟机的起始MN编号 (default 0)
--sampling-interval duration *模拟机的采样时间 (default 1s)
--timestamp-start string *模拟机开始采样的时间 (RFC3339) (default "2018-01-01T00:00:00Z")
--timestamp-end string *模拟机采样结束数据 (RFC3339) (default "2018-01-02T00:00:00Z")
--seed int *全局随机数种子(设置为0是使用当前时间作为随机数种子) (default 12345678)
--batch-size int 1个http请求中携带Point个数 (default 100)
--gzip int 是否使用gzip,level[0-9],小于0表示不使用 (default 1)
--workers int 并发的http个数 (default 1)
--time-limit duration 最大测试时间(-1表示不生效),>0会使参数timestamp-end失效 (default -1ns)
--debug 是否打印详细日志(default false).
--cpu-profile string 将cpu-profile信息写入文件的地址,用于自测此工具
--do-db-create 是否创建数据库 (default true)
注意:带*的参数叫做信息元参数,其他为运行参数
例如:使用以下命令
fcbench write --use-case vehicle --scale-var 1000 --sampling-interval 10s --urls http://localhost:8086
上述命令表示使用车载(vehicle)场景,模拟1000辆车,每个车采样时间间隔10s,在默认时间范围2018-01-01T00:00:00Z~~~2018-01-02T00:00:00Z的写入默认数据库benchmark_db中。
fcbench write这个命令集合了数据生成和数据写入两个过程,在这个过程中如果发现数据库不存在,会自动创建数据库。
如果已有数据库,不想创建数据库,可以添加--do-db-create=false
2.2 查询测试
使用fcbench query命令可以进行查询测试,它需要先使用2.1中的命令将数据写入到数据库进行测试。
注意: 为了保证查询语句能命中数据,以下参数中带*号的信息元参数必须和fcbench write保持一致。
Flags:
--urls string *被测数据库的地址 (default "http://localhost:8086")
--db string *数据库的database名称 (default "benchmark_db")
--use-case string *使用的测试场景(可选场景: vehicle, air-quality, devops) (default "vehicle")
--scale-var int *场景的变量,一般情况下是场景中模拟机的数量 (default 1)
--scale-var-offset int *场景偏移量,一般情况下是模拟机的起始MN编号 (default 0)
--sampling-interval duration *模拟机的采样时间 (default 1s)
--timestamp-start string *模拟机开始采样的时间 (RFC3339) (default "2018-01-01T00:00:00Z")
--timestamp-end string *模拟机采样结束数据 (RFC3339) (default "2018-01-02T00:00:00Z")
--seed int *全局随机数种子(设置为0是使用当前时间作为随机数种子) (default 12345678)
--batch-size int 1个http请求中携带查询语句个数 (default 1)
--gzip int 是否使用gzip,level[0-9],小于0表示不使用 (default 1)
--workers int 并发的http个数 (default 1)
--query-type int 查询类型 (default 1)
--query-count int 生成的查询语句数量 (default 1000)
--time-limit duration 最大测试时间(-1表示不生效),>0会使query-count参数失效 (default -1ns)
--debug 是否打印详细日志(default false).
--cpu-profile string 将cpu-profile信息写入文件的地址,用于自测此工具
--do-db-create 是否创建数据库 (default true)
这些参数在默认情况下是一致的,如果有修改,需要额外注意.
一个执行查询测试的例子: 1、我们使用list命令可以查看所有内置的场景以及支持的查询类型。
fcbench list
--detail可以获取到更多的关于说明。
2、写入数据,注意信息元参数
fcbench write --use-case vehicle --scale-var 1000 --sampling-interval 10s --urls http://localhost:8086
3、对应步骤2章节的查询,信息元参数一致,如下:
fcbench query --query-type 1 --use-case vehicle --scale-var 1000 --sampling-interval 10s --urls http://localhost:8086 --query-count 1000
查询测试提供两种结束控制,一种是以查询语句数量,使用--query-count标签;另一种是使用运行时间,--time-limit标签。
注意:一旦使用--time-limit标签,--query-count就无效了。例如下面测试5分钟:
fcbench query --query-type 1 --use-case vehicle --scale-var 1000 --sampling-interval 10s --urls http://localhost:8086 --time-limit 5m
2.3 混合读写
使用fcbench mixed命令可以进行混合查询测试,支持的参数如下:
Flags:
--urls string *被测数据库的地址 (default "http://localhost:8086")
--db string *数据库的database名称 (default "benchmark_db")
--use-case string *使用的测试场景(可选场景: vehicle, air-quality, devops) (default "vehicle")
--scale-var int *场景的变量,一般情况下是场景中模拟机的数量 (default 1)
--scale-var-offset int *场景偏移量,一般情况下是模拟机的起始MN编号 (default 0)
--sampling-interval duration *模拟机的采样时间 (default 1s)
--timestamp-start string *开始测试前准备数据的开始时间 (RFC3339) (default "2018-01-01T00:00:00Z")
--timestamp-prepare string *开始测试前准备数据的结束时间 (RFC3339) (default "2018-01-01T00:10:00Z")
--seed int *全局随机数种子(设置为0是使用当前时间作为随机数种子) (default 12345678)
--batch-size int 1个http请求中携带Point个数 (default 100)
--gzip int 是否使用gzip,level[0-9],小于0表示不使用 (default 1)
--workers int 并发的http个数 (default 1)
--mix-mode string 混合模式,支持parallel(按线程比例混合)、request(按请求比例混合) (default "parallel")
--query-type int 查询类型 (default 1)
--query-percent int 查询请求所占百分比 (default 0)
--query-count int 生成的查询语句数量 (default 1000)
--time-limit duration 最大测试时间(-1表示不生效),>0会使参数timestamp-end失效 (default -1ns)
--debug 是否打印详细日志(default false).
--cpu-profile string 将cpu-profile信息写入文件的地址,用于自测此工具
--do-db-create 是否创建数据库 (default true)
一种典型的混合场景测试过程如下:
1、执行以下命令,注意timestamp-start、timestamp-prepare参数,表明测试混合前先准备timestamp-start到timestamp-prepare的数据
fcbench mixed --query-type 1 write --use-case vehicle --scale-var 1000 --timestamp-start "2018-01-01T00:00:00Z" --timestamp-prepare "2018-01-02T00:00:00Z" --sampling-interval 10s --urls http://localhost:8086
2.4 数据写入的其他方式
2.1和2.2中的write和query命令都是使用协程,一边生成数据,一边写入。 经过调优,4核情况下,vehicle场景能支撑480 000 points/s的生成速度。air-quality场景能支撑1 000 000 points/s
我们注意到influxdb-comparisons是先生成数据到文件或者stdout,再由另一个工具写入到数据库。 因此参照influxdb-comparisons工具,也提供以下类似的命令,仅用来进行对比, 已经隐藏, 不对外展示。
数据生成:
fcbench data-gen --use-case vehicle --scale-var 1000 --sampling-interval 10s >> data.txt
数据写入:
fcbench data-load --urls http://localhost:8086 --file data.txt
查询语句生成:
fcbench query-gen --use-case vehicle --scale-var 1000 --sampling-interval 10s >> query.txt
查询语句查询:
fcbench query-load --urls http://localhost:8086 --file query.txt
2.5 高级功能-调度器
使用fcbench schedule命令可以连续执行多次测试,配合需要使用fcbench agent命令
一般情况下,我们的测试拓扑如下:
测试机(fcbench-schedule) ------- 被测机(fctsdb数据库+fcbench-agent)
为了支持两次测试间清理数据库:fcbench agent命令提供了一种方式,可以在被测机上,开启、关闭、清理数据库。 按照以下步骤进行测试:
1、被测机启动agent
fcbench agent --fctsdb-path /root/fctsdb/v16n/fctsdb --fctsdb-config /root/fctsdb/configs/test.conf
2、可以查看调度器内置的配置文件,写入到testcase.txt:
fcbench schedule list > testcase.txt
根据需求修改testcase.txt, 根据配置文件运行多次测试, 结果会记录在一个最新时间为名字的csv中,例如benchmark_1013_173901.csv:
fchench schedule --agent http://{被测机ip}:端口 --grafana http://10.10.2.30:8888/sources/1/dashboards/3 --config-path testcase.txt
其中--grafana是监控前端地址, 用来拼接完整的监控地址: http://10.10.2.30:8888/sources/1/dashboards/3?refresh=Paused&tempVars%5Bhost%5D=10.10.2.29&lower=2022-09-07T10%3A36%3A53Z&upper=2022-09-07T10%3A41%3A53Z
新增功能
testcase.txt文件支持动态设置agent端的fctsdb数据库路径和config文件路径
实现方式是在文件中添加内容,一个典型的例子如下:
{"Group":"车载Series变化","MixMode":"write_only","UseCase":"vehicle","Workers":64,"BatchSize":1000,"ScaleVar":1,"SamplingInterval":"1s","TimeLimit":"5s","UseGzip":1,"QueryPercent":0,"PrePareData":"","NeedPrePare":false,"Clean":true,"SqlTemplate":null}
$Set {"BinPath":"/root/fctsdb/fctsdb", "ConfigPath":"/root/fctsdb/config"}
{"Group":"车载Series变化","MixMode":"write_only","UseCase":"vehicle","Workers":64,"BatchSize":1000,"ScaleVar":1000,"SamplingInterval":"1s","TimeLimit":"5s","UseGzip":1,"QueryPercent":0,"PrePareData":"","NeedPrePare":false,"Clean":true,"SqlTemplate":null}
3、使用以下命令可以将两次测试生成的csv进行对比, 并生成对比html测试报告:
./fcbench schedule create ~/result/fctsdb-amd/v15n.csv ~/result/fctsdb-amd/v16n.csv --out write-v15n-v16n.html
2.6 高级功能-mock
使用fcbench mock支持mock一个海东青数据库,用以测试环境是否达标。
3 代码结构
3.1 文档目录
.
├── agent agent功能的client和service代码目录
│ ├── client.go agent功能client代码
│ ├── fctsdb_handlers.go agent功能service中fctsdb的handle
│ ├── influxdbv2_handlers.go agent功能service中influxdbv2的handle
│ ├── matrixdb_handlers.go agent功能service中matrixdb的handle
│ ├── mysql_handlers.go agent功能service中mysql的handle
│ ├── opentsdb_handlers.go agent功能service中opentsdb的handle
│ └── service.go agent功能service中外层代码
├── buildin_testcase schedule命令内置测试用例以及这些用例的html对比测试报告生成器
│ ├── report.go 对比报告生成器
│ ├── testcase.go 内置用例定义文件
├── cmd 命令行、运行框架文件
│ ├── agent.go agent命令的实现
│ ├── basic_bench_task.go benchmark运行框架,定义了基础的一次性能测试的全部流程,关联write、query、mixed三个命令
│ ├── command.go write、query、mixed三个命令的定义文件
│ ├── data_gen.go data_gen隐藏命令的实现
│ ├── data_load.go data_load隐藏命令的实现
│ ├── main.go 程序入口文件
│ ├── qps.go basic_bench_task所需的qps处理文件
│ ├── query_gen.go query_gen命令的实现
│ ├── query_load.go query_load命令的实现
│ └── scheduler.go scheduler命令的实现
├── data_generator 不同场景数据生成模块,生成的结果对象为common子模块的point对象
│ ├── airq 空气质量场景的数据与sql生成器模块
│ ├── common 所有场景所需的通用抽象
│ ├── dashboard dashboard场景,来源于influxdb-comparisons,暂未完全适配我们框架
│ ├── devops devops场景,来源于influxdb-comparisons,暂未完全适配我们框架
│ ├── iot iot场景,来源于influxdb-comparisons,暂未完全适配我们框架
│ ├── live 生活消费场景,临时测试
│ ├── metaqueries metaqueries场景,来源于influxdb-comparisons,暂未完全适配我们框架
│ ├── universal universal--万能场景, 根据一些关键数量生成数据, 例如"{\"MeasurementCount\":2000,\"TagKeyCount\":1,\"FieldsDefine\":[40,40,20]}"
│ └── vehicle 车载场景的数据与sql生成器模块
├── db_client 数据库初始化、创建db、写入、查询、序列化器的模块
│ ├── common.go
│ ├── fctsdb_client.go
│ ├── influxdbv2_client.go
│ ├── matrixdb_client.go
│ ├── mysql_client.go
│ └── opentsdb_client.go
├── query_generator 内置的场景查询语句模板
├── report 对比测试报告中需要用的简单组件渲染抽象
│ ├── page.go 页面渲染,包括标题、测试组等
│ ├── picture 图形组件渲染,折线图、柱形图等等
│ ├── src 渲染所需js等资源文件
│ └── table 表格组件渲染
└── util 存放一些用到的小模块
├── fastrand 一个快速的rand模块,使用golang runtime中的相关函数,协程安全且性能极高
├── gbt2260 **地理位置编码
└── keydriver 关键字驱动,未实现
3.2 写入数据核心设计思路
1、data_generator模块生成数据,产出对象为data_generator/common/point.go中的point对象
type Point struct {
MeasurementName []byte #表名字
TagKeys [][]byte #tag名字
TagValues [][]byte #tag的值
FieldKeys [][]byte #field名字
FieldValues []interface{} #field值
Int64FiledKeys [][]byte #int64类型field名字,特例化,加速int64类型的转换
Int64FiledValues []int64 #int64类型field值,特例化,加速int64类型的转换
Timestamp *time.Time #时间搓
}
其中,对int64这种类型单独存储,是为了减少vehicle这种场景在大量int64的field情况下,转换成interface{}的时间消耗,提升性能。
2、point对象由db_client中不同的数据库进行序列化,主要是db_client/common.go的DBClient对象的以下三个方法。
// 序列化器,序列化一个batch为目标,分为三个阶段。返回结果是append到一个bytes数组中。
// 1、准备阶段,添加一些头信息或者类似mysql的列信息
BeforeSerializePoints(buf []byte, p *common.Point) []byte
// 2、序列化一个point对象,并把添加到bytes数组中
SerializeAndAppendPoint(buf []byte, p *common.Point) []byte
// 3、batch的尾部内容,例如一些结束符;等等
AfterSerializePoints(buf []byte, p *common.Point) []byte
通过这三个方法,最后生成的结果一个是byte数组,包含一个batch的数据。
3、将步骤2中序列化后的byte数组进行发送,调用的db_client/common.go的DBClient对象的write方法。
Write(body []byte) (int64, error)
3.3 查询数据核心设计思路
1、在data_generator/common/sql_temlate.go文件中,设计了一种简单的模板替换。
// 举个例子:
// "select mean(aqi) as aqi from city_air_quality where city in '{city*6}' and time >= '{now}'-30d group by time(1d)"
// 将被分割成base段: "select mean(aqi) as aqi from city_air_quality where city in '"、"' and time >= '"、"'-30d group by time(1d)"三个
// 关键字: city、now
// 重复次数: 6、1
对应这个例子,在air场景中的data_generator/airq/generate_data.go文件的nextSql方法进行关键字替换,下列这个方法
func (s *AirqSimulator) NextSql(wr io.Writer) int64
最终生成下面这个完整sql:
select mean(aqi) as aqi from city_air_quality where city in '百色市','德宏傣族景颇族自治州','大连市','济宁市','佳木斯市','石家庄市' and time >= '2021-01-01T00:01:40+08:00'-30d group by time(1d)
完整的例子可见data_generator/airq/airq_test.go
2、将步骤1中得到sql进行发送,调用的db_client/common.go的DBClient对象的query方法。
Query(body []byte) (int64, error)
3.4 主要流程调用链
最主要的测试流程的调用链如下图:
cmd/main.go
│
├── cmd/command.go ───┐
│ ├──> basic_bench_task.go
├── cmd/scheduler.go ───┘ ├──> data_generator(*.go) ──> util(*.go)
│ │ └──> db_clent(*.go)
│ └──> agent/client.go
│
└── cmd/agent.go ───> agent/serivce.go ───> agent/*_handlers.go
html测试报告生成功能调用链
cmd/main.go
└── cmd/scheduler.go ───> buildin_testcase/report.go ───> reprot/*.go
新加数据库,主要添加一个db_client文件