我尝试创建一个Go语言的基准测试工具的故事
这篇文章是 Applibot Advent Calendar 2021 的第13篇文章。
昨天是@dears31先生的文章《通过Zoom评论汇总自由回答的测验》。
首先
你好,我是一名在应用机器人中担任服务器端工程师的人。
最近,我在日常工作中开始逐渐增加测量服务性能并将结果汇总作为技术选型的资料等工作。
最近的一个例子是测量Elasticache Redis的性能。
我认为最近Aurora3的推出,TiDB和Spanner等分布式数据库的采用案例增加,以及KeyDB开始作为托管服务提供,这些都是技术选择的机会,可能会导致现有技术的大规模更替。在引入新技术时,我一直希望从成本、运营和性能等多个方面进行评估。
为了了解性能方面的情况,我认为不仅要阅读文档,还需要使用基准测试工具进行实际测量。
有许多不同的基准测试工具可用。例如,对于Redis,有官方工具redis-benchmark以及适用于memcached的memtier_benchmark。对于HTTP类基准测试工具,我认为Apache Benchmark很著名。
每个工具的界面都不同,可能会使结果总结变得复杂,有时甚至可能没有合适的工具可用。
此外,仅通过文档无法获得细节行为的完整了解,而根据实现语言进行解读可能会很困难,有时也希望自定义场景…
为了满足这些建议,我打算使用我熟悉的Go语言来开发基准测试工具,并在开发过程中将相关经验整理成一篇文章。
※我们并没有开发出能够满足所有这些需求的工具,只是刚刚开始开发而已。
规格
-
- 性能を見るために必要なクライアント側の指標
Latency(avg, min, max, %tile)
RPS
シナリオのカスタマイズ性
最終レポートを出力出来る機能
这一次,我会尝试实施上述内容。
另外,我还会实现可以对Redis进行基准测试的功能。
在实际验证过程中,需要同时监视服务端的CPU使用率和延迟等,但要实现这一点可能会非常复杂,所以本工具将不包含该功能。
实施
我把成果物放在了Github上。
源代码尚未整理,我在有时间的时候考虑整理一下,所以这次请见谅。
開発したものは、下記のインターフェースが重要になっています。
type Executor interface {
Setup(context.Context) (context.Context, error)
ExecContext(context.Context) (context.Context, error)
}
type Preparer interface {
PrepareContext(context.Context) (context.Context, error)
}
执行者设想了一个像一个情节一样的东西。
Setup メソッドは前処理になります。
Setup はベンチマークを開始する前に、1度実行され、RedisへのGETコマンドのベンチマークの場合は、ここでデータを挿入し、キャッシュヒット率をあげたりすることができるようになっています。
ここで、Setup がcontext.Contextを返却しているのは、前処理段階でした情報をcontext上に持たせたいケースを想定して、返却するようにしています。
たとえば、「RedisのGETメソッドのキャッシュヒット率を100%にしたい、ただキーはランダムで生成したい」などといったユースケースがあると、Setupで事前に発行したランダムなキーで、RedisへSETを行い、そのSETしたキーをcontextに詰め、実際にGETする際にcontextからキーを取得し、GETを行う
といった処理を書くと、実現できるようになります。
ExecContext メソッドは、実際に計測する処理を実装する部分になります。
Redisで考えると、ここは実際にGETメソッドを発行することになります。
このメソッドがcontextを返す理由は現状ほぼないです。
今後、一連の処理を1つずつ計測すると言った際に、ExecContextの結果を持ったまま、次の処理にいくなどと言った際にひつようになるため、追加しています。
※YAGNIです。
続いて、Preparer ですが、こちらは ExecContext で必要な情報を準備する処理を行うinterfaceになります。
準備処理が結果のレイテンシに影響しないように、インターフェースを分けています。
Preparerは実装されてる場合のみ、呼ばれるようになります。
通过基本上添加这个接口,您可以实现除Redis之外的其他实现。
その他は、特に解説しないので、詳しくはソースコードを参照してください。
验证
现在,我们就来实际验证一下。
这次我们实现了Redis的基准测试工具,作为比较,我们将使用redis-benchmark进行验证。
详细环境
Redis使用了Elasticache,使用了Redis 6.x系列,并禁用了Redis集群模式。Redis使用了r6g.large实例类型。
进行基准测试的机器使用了EC2实例,实例类型为c6g.4xlarge。
两者都准备了一台。
在这次基准测试中,我们更关注Elasticache Redis自身的延迟,而非跨越az的延迟。我们想验证验证工具本身是否有效,因此将az设置为ap-northeast-1a。
对Redis进行性能测试的内容
-
- GETコマンド
-
- キャッシュヒット率0%
- 同時接続クライアント500
本次未考虑的内容
-
- キーの長さ
- 実行時間
实测
- 在 Redis 基准测试中的结果。
命令
$ redis-benchmark -h $HOST -t get -c 100 --threads 5 -n 40000000
结果-
...
throughput summary: 253900.53 requests per second
latency summary (msec):
avg min p50 p95 p99 max
0.380 0.088 0.375 0.519 0.591 59.455
RPS 25万,ElasticacheRedis真強啊!哪個網絡延遲,平均、p95、p99都表現得相當出色啊!
2. 本次工具的结果
命令
cmd bench --threads=500 --clients=500 --host=$HOST --duration=180 --output-json-path=result.json
结果 –
...
{
"avg(ms)": 1.953908553753247,
"max(ms)": 9.575562,
"min(ms)": 0.123043,
"p50(ms)": 1.8975165033781725,
"p90(ms)": 2.048262475701852,
"p95(ms)": 2.1775845942189624,
"p99(ms)": 3.569607007901278,
"rps": 222619,
"start_time": "2021-12-12T15:22:48.817915446Z"
},
...
嗯,Redis压测性能没有那么好 (^_^;) RPS也相差了3万,延迟也相差了4到6倍呢。。。
※本次实施的工具,并非在所有时间间隔内进行汇总,而是仅每秒进行汇总,因此所记载的结果仅为单个时间点的结果。
总结
这次我们开发了一个可以广泛应用的基准测试工具。
哎呀,这个性能不及redis-benchmark啊 (^_^;)
很抱歉以目前的狀況無法完成這篇文章。由於缺乏時間和精力進行原因追究。
本次创建的工具在执行 redis-benchmark 时,EC2 的 CPU 使用率约为 300% 左右,而我们创建的工具达到了约 600% 左右。与 redis-benchmark 相比,我们的工具不仅存在负载问题,而且结果也无法达到预期。在 Go 代码中,我们使用了 goredis,但尝试将其更改为直接使用 TCP 客户端的实现,并没有明显影响,不知道瓶颈在哪里… 如果有人了解,请留下评论,谢谢!
未来
我对我们这次开发的工具感到有些失望,因为它的精度还不够高,同时开发也相当困难。另外,在整理结果时,还无法完整提供所需的信息等等,所以完善度还有待提高。
希望未来能够在重构的同时,能够快速追加开发所需的功能,并使其能够随时使用。