VictoriaLogs 运营数据分享
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!
- cnblogs博客
- zhihu
- Github
- 公众号:一本正经的瞎扯
![]()
VictoriaLogs 是一个高性能的日志存储组件。
本文分享一组 VictoriaLogs 单机版的生产环境运营数据。
从本文可以得知:
- VictoriaLogs 的性能到底有多优秀
- 日志存储引擎的内部运行细节
- 如何根据业务数据调优
VictoriaLogs 的 Metrics 指标定义请参考:https://docs.victoriametrics.com/victorialogs/metrics/
背景
生产环境中,部署了一个 VictoriaLogs 的单机版的容器,来接收所有业务产生的日志。
选择全天 CPU 最高峰的时间点,记录这个时间点对应的所有指标的数据。
因为这个时间点正好没有查询,所以主要体现日志写入的性能。
配置
| 大类 | 配置项 | 数值 | 说明 |
|---|---|---|---|
| 服务器资源 | CPU cores | 48 | 指标 vm_concurrent_insert_capacity 的值为 96, 这里体现了写入并发的上限 |
| 内存 | 64 GB | ||
| 磁盘 | 16TB | ||
| 进程配置 | 存储周期 | 7 天 | 指标 vl_partitions 的值为 8。 分区数 8,因为每天一个分区文件夹,七天存储周期最多出现 8 个分区。 |
| 版本 | v1.28.0 |
运行情况
CPU
| 数据项 | 数值 | 说明 |
|---|---|---|
| CPU 占用 | 25.1 核 | 占总 CPU 数的 52.3% 指标为 process_cpu_seconds_total。 container_cpu_usage_seconds_total 这个指标统计为 25.6 核,此处里容器内统计到的信息为准。 |
| 物理线程数 | 139 | 因为使用了 zstd 这个库,cgo 的特性引起了物理线程过多。 |
| 协程数 | 101 | 协程数比物理线程还要少。说明使用协程非常克制。赞! |
内存
| 数据项 | 数值 | 说明 |
|---|---|---|
| container_memory_usage_bytes | 61476073472 bytes 57.25 GB |
容器总计使用的内存。 占容器分配内存的 89.45% |
| container_memory_working_set_bytes | 38457155584 bytes 35.8GB |
活跃内存 |
| container_memory_cache | 52578189312 bytes 48.97 GB |
用于文件 cache 的内容,占分配内存的 76.52% container_memory_mapped_file 为 42.42 GB 包含在 memory_cache 之内 |
| container_memory_rss | 8484524032 bytes 7.9 GB |
实际使用的物理内存,占分配内存的 12.34% |
实际使用的物理内存再细分,组成如下:

Golang runtime 的 heap 组成情况如下:

VictoriaLogs 的内存分配优化得非常好,下图可以体现:

平均每秒才分配 40MB 左右的内存,说明很多地方都使用了 sync.Pool 这样的对象池。
存储
存储使用了阿里云上的 VPC.
| 数据项 | 数值 | 说明 |
|---|---|---|
| 已使用磁盘空间 | 12372666871808 bytes 11.25 T |
占总空间的 70.3% |
| 句柄数 | 5230 | 暂无法区分文件 fd 和 socket |
| rows_dropped_total | 0 | 说明一直以来都没有发生日志丢弃的情况 |
| 磁盘每秒读量(逻辑) | 123999970 bytes/s 118.26mb/s |
来自指标 vm_filestream_real_read_bytes_total 磁盘总在均匀的读写,说明后台的 merge 一直在平稳的运行。 |
| 磁盘每秒写量(逻辑) | 153990664 bytes/s 146.86 mb/s |
来自指标 vm_filestream_real_written_bytes_total |
| 磁盘每秒读次数(逻辑) | 1893 次/s | vm_filestream_real_read_calls_total |
| 磁盘每秒写次数(逻辑) | 1176 次/s | vm_filestream_real_write_calls_total |
| 磁盘块设备读取量(物理) | 87431646 bytes/s 83.38 mb/s |
container_fs_reads_bytes_total 指标来自 cAdvisor 或 containerd / kubelet 的统计数据,通常对应 cgroup 的 blkio 层。它反映的是 容器内所有进程在底层块设备上的实际 I/O 统计。只有真正命中块设备 的 I/O 才会被统计,内核页缓存(page cache)中的读写不会立即计入这些计数。 |
| 磁盘块设备写入量(物理) | 131454020 bytes/s 125.36 mb/s |
container_fs_writes_bytes_total 实现原理同上 |
| 磁盘块设备读取次数(真实 IOPS) | 1297 次/s | container_fs_reads_total 从次数看,存储设备一定是 SSD; 机械磁盘无法承受这么大的 IOPS |
| 磁盘块设备写入次数(真实 IOPS) | 1880 次/s | container_fs_writes_total 操作系统定期把脏的 page cache 写入磁盘,所以 container_fs_writes_total 与 vm_filestream_real_write_calls_total 并没有关联关系。 |
网络
| 数据项 | 数值 | 说明 |
|---|---|---|
| 网络入流量 | 520 mb/s | 来自指标 container_network_receive_bytes_total |
| 网络出流量 | 124 kb/s | 来自指标 container_network_transmit_bytes_total |
| 日志数据的流量 | 465881848 bytes/s 444.3 mb/s |
来自 vl_bytes_ingested_total |
数据摄入 (data ingested)
| 数据项 | 数值 | 说明 |
|---|---|---|
| 收到的日志速率 | 491641 条 / s | 来自指标 vl_rows_ingested_total |
| insert_processors_count | 11 | 负责写日志的并发协程数 |
| vl_too_long_lines_skipped_total | 0 | 写入过程中没有丢弃数据 |
索引数据量
VictoriaLogs 中,指定为 stream field 的字段会被写入索引中。
| 数据项 | 数值 | 说明 |
|---|---|---|
| 收到的日志速率 | 491641 条 / s | 来自指标 vl_rows_ingested_total |
| 索引条数 | 83180 | 来自指标 vl_indexdb_rows |
| 索引的磁盘容量 | 2601009 bytes 2.48 mb |
vl_data_size_bytes |
日志数据量
日志的所有 tag 和 _msg,及其相关的 bloom filter 位图,都会写入 VictoriaLogs 的数据部分。
| 数据项 | 数值 | 说明 |
|---|---|---|
| Big 分区日志条数 | 204037001713 | vl_storage_rows |
| small 分区日志条数 | 699306811 | vl_storage_rows |
| Inmemory 日志条数 | 13792407 | vl_storage_rows |
| 日志的磁盘容量 | 12163334662045 bytes 11.06 TB |
vl_data_size_bytes |
Merge 相关
| 数据项 | 数值 | 说明 |
|---|---|---|
| merge 并发数:storage/inmemory | 13 | 相当于把内存中缓存的块写入磁盘. 指标:vm_concurrent_insert_current |
| merge 并发数:storage/big | 3 | |
| merge 并发数:storage/small | 2 | |
| Merge 次数:storage/inmemory | 921 次 / 分钟 | Aka: 15.35 次/s 指标:vl_merges_total |
| Merge 次数:storage/big | 1 次 / 分钟 | |
| Merge 次数:storage/small | 5 次 / 分钟 | |
| Merge 日志行数:storage/inmemory | 1350000 条 / s | 指标:vl_rows_merged_total |
| Merge 日志行数:storage/big | 0 条 / s | 说明大分区并不是时时刻刻都有要合并的数据 |
| Merge 日志行数:storage/small | 452000 条 / s |
以上数据可以看出:
-
Inmomery 的 merge 并发量和日志处理数量是最多的。显而易见,日志会先写入内存,做各种日志特征合并后,再定期写入磁盘。这是提升性能的必然方法。
-
Small 的分区需要维持一定的 merge 处理量,毕竟 inmomery block 数据是因为避免因为断电而需要快速落盘。落盘后,这些压缩比不够高的数据,需要由 merge 协程来把数据合并到压缩比更高的 big 分区中。
-
我认为:在保障正常写入和正常查询的前提下,big 分区的 merge 可以再激进一些。把剩余的 CPU 和内存都利用起来,实现日志的更高的压缩比。
结论
CPU 方面
已经使用的核数为 25.1,每秒进入的日志数据量为 444.3 mb/s,则粗略推断:
- 单核极限日志写入性能为:17.7 mb / s
已经知道每秒的日志条数为 491641 条 / s,则每条日志平均大小为:465881848 / 491641 = 947.6 字节/条。
粗率计算为:
- 在单条日志接近 1kb/条的情况下,单核的极限日志写入性能为:19587 条 / s
VictoriaLogs 在并发设计方面非常优秀,物理线程数和协程数相比分配的核数而言并不多。
通过统计 increase(go_mutex_wait_seconds_total{pod="xxx"}[1m])发现,每分钟一共花费了 1.64 秒用于等待锁,平均到每个核上就是 1.64 / 48 = 34 毫秒,意味着每个核平均每分钟花了 34 毫秒来等待锁。
内存方面
VictoriaLogs 的内存管理做得非常优秀,从单位时间的内存分配量和 GC 次数方面来看,内部通过大量使用对象池而有效降低了:
- 内存分配的消耗
- GC 的消耗
- 内存量的抖动
整体内存方面,虽然看起来使用了 64GB 物理内存中的 57.25 GB,但进程使用的物理内存仅仅只有 7.9GB(占 12.34%),其余的内存都是文件 cache 占用的(随时可以淘汰出去提供给进程内存使用)。
结论:在不考虑查询性能影响的情况下,VictoriaLogs 的 CPU 与内存的配比可以调整为 2:1,也就是每两个 CPU 对应着 1GB 内存。
从 inmomery 的日志条数 13792407 看,下文测算出压缩后单条日志的平均长度为 59.4 bytes,则缓存日志占用的空间是 781 MB,看起来缓存这个量级的日志导致的内存压力并不大。
网络方面
可以看出,没有查询的时候,几乎没有网络的出流量。
这里一个值得调优的地方是:可以在负责日志发送的 Vector 进程上配置 gzip 压缩,可能会导致 VictoriaLogs CPU 有少量的上升,但整体的资源占用费率可能更低。
VictoriaLogs 的主要资源瓶颈是 CPU,相比之下网络流量的消耗并不大。
存储引擎的索引方面
虽然 7 天的日志数据占用磁盘空间为 11.25 T,但磁盘上的索引占用的空间仅仅只有 2.48 mb,实在是小得难以置信。(我特地跟踪了源码,从逻辑上看,metrics 确实上报了真实的索引大小)
同时,索引的条数也非常少,仅仅只有 83180 条。
很小的索引得益于非常克制地在 stream field 中配置了很少,且变化不频繁的字段。索引仅仅只在第一次发现新的 stream field 的情况下创建,因此索引的增量与 stream field 的变化量是正相关的。
查询发现:
-
increase(vl_streams_created_total{}[5m])每 5 分钟才产生 1-2 个新的 stream,由此说明索引的增量也非常小,从而七天内的索引也很小。 -
Stream 的增量小,同时也提升了单位时间的日志写入量,相当于索引部分不需要写入任何数据。
这里也可以看出我们自身业务配置上的问题:
stream field 的配置过于克制了,虽然减少了索引,但是查询时可能会因为索引数不够而只能在 block 种类很少但是整体数据量很大的规模上进行搜索。stream 的增量不足会导致索引创建不足,这里需要平衡。
存储引擎的数据方面
日志数据的磁盘空间为 11.06 TB,big 分区的日志数加上 small 分区的日志数,得到磁盘上的平均日志长度为:59.4 bytes/条。
相比数据摄入时计算得到的平均日志长度 947.6 字节/条,经过 VictoriaLogs 处理后,日志的压缩倍率为:947.6 / 59.4 = 16 倍。
总结
- VictoriaLogs 每核的极限写入性能是:17.7 mb / s
- 在日志平均长度在 1kb 左右时,每核的极限写入性能是 19587 条 / s
- 部署时,可以每 2 个核对应 1GB 内存来分配资源
- VictoriaLogs 对磁盘的 IOPS 仍然很高,需要使用 SSD 这样的存储设备
- Stream Field 的配置要合适,避免索引增长太快,也要避免索引太少。
- 在 vector 日志采集端,加上日志数据的 gzip 压缩更好
对于 VictoriaLogs 的开发团队,我想说:
-
希望可以在数据压缩上更激进,且内存的缓冲时间更长 —— 以便在 HDD 机械硬盘上也有较好的性能表现 —— 最终,日志存储的成本可以进一步降低。
-
对于 StreamField,我希望可以增加一个类似于 IndexField 的配置:
- 一方面,我可以控制数据部分的 block 的种类,从而保持更大的数据压缩比
- 另一方面,业务可以根据需要把某些 tag 加入索引,从而提高查询性能
- 同时,stream field 仅仅在第一次出现时创建索引,导致后续的 非 stream field 中的 tag value 不再写入入索引。如果引入 IndexField 的机制,可以避免这种情况。
-
cache 方面的 metrics 上报太少,并且要给出 cache turning 的命令行参数。
-
在保障正常写入和正常查询的前提下,big 分区的 merge 可以再激进一些。把剩余的 CPU 和内存都利用起来,实现日志的更高的压缩比。
感谢 VictoriaLogs 团队,这个组件的性能和稳定性让人惊艳。
(不过,你们拒绝了我用 Plan9 汇编优化性能的 PR,I still upset! 😦 )
links
关于 VictoriaLogs 的内部实现原理,我写了一篇文档:InsideVictoriaMetrics : 8_VictoriaLogs基础知识.md
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/945428.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!