作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


本文是为了解决Victoria-Metrics中的过载问题而得出的较好实践。也是为了回答这样一些问题:

  • vm-storage的单核写入性能的极限在哪里?(请见之前的三篇文章)
  • 怎么样才能感知到,vm-insert到vm-storage这条写入链路上已经发生了过载?
  • 如果写入链路发生过载,是否会丢数据?如果丢数据,究竟在哪个环节丢数据?过载了如何做保护,如何避免雪崩?

下面开始回答以下问题:

1.vm-storage的单核写入性能的极限在哪里?

  • 全是新metric的情况:6000/核/s
  • 全是旧的metric的情况:43万/核/s
  • 新metric占整体1%的情况:24.6万/核/s

此外:写入性能是否会随着时间推移索引数增多后,导致写入性能下降?

会的!原因在于内存占用量升高,tsid cache命中率降低导致。

只要活跃的metric都在tsid cache中,写入性能就不会受到影响。

2.写入链路过载感知

2.1 vm-storage中,几乎不会发生丢数据的情况

统计drop相关的metric,可以看出vm-storage上丢弃的数据量:

sum by () (rate(vm_hourly_series_limit_rows_dropped_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.+"})) +
sum by () (rate(vm_daily_series_limit_rows_dropped_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.+"})) +
sum by () (rate(vm_concurrent_addrows_dropped_rows_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.+"}))

或者,把接收到的行数为分母,把最终写入存储的行数为分子,可以计算出vm-storage上的写入成功率:

sum by () (rate(vm_rows_added_to_storage_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.+"}[1m])) /
sum by () (rate(vm_vminsert_metrics_read_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.+"}[1m]))

在没有刻意限制每小时和每天的新metric数量的情况下,实测过程中没有发现任何vm-storage上的数据丢弃。

2.2 vm-insert中的数据丢弃逻辑:

通过配置 -dropSamplesOnOverload 来丢弃数据

see: app/vminsert/netstorage/netstorage.go:53

func (sn *storageNode) push(buf []byte, rows int) error {
if len(buf) > maxBufSizePerStorageNode {
logger.Panicf("BUG: len(buf)=%d cannot exceed %d", len(buf), maxBufSizePerStorageNode)
}
sn.rowsPushed.Add(rows)
if sn.trySendBuf(buf, rows) {
// Fast path - the buffer is successfully sent to sn.
return nil
} // 只要缓冲区写满,且配置了丢弃数据,就会直接丢弃数据。丢弃数据能够避免vm-insert OOM崩溃
if *dropSamplesOnOverload && atomic.LoadUint32(&sn.isReadOnly) == 0 {
sn.rowsDroppedOnOverload.Add(rows)
dropSamplesOnOverloadLogger.Warnf("some rows dropped, because -dropSamplesOnOverload is set and vmstorage %s cannot accept new rows now. "+
"See vm_rpc_rows_dropped_on_overload_total metric at /metrics page", sn.dialer.Addr())
return nil
}
// Slow path - sn cannot accept buf now, so re-route it to other vmstorage nodes.
if err := sn.rerouteBufToOtherStorageNodes(buf, rows); err != nil {
return fmt.Errorf("error when re-routing rows from %s: %w", sn.dialer.Addr(), err)
}
return nil
}

由源码可知:当vm-storage达到瓶颈,vm-insert与vm-storage之间的传输必然变慢,变慢后导致netstorage客户端的缓冲区无法及时释放。这个时候如果配置了-dropSamplesOnOverload选项,就会直接丢弃数据。

丢弃数据后,通过metric vm_rpc_rows_dropped_on_overload_total 会体现出丢弃的量。

虽然这个配置项能够避免vm-insert发生OOM(Out of memory)崩溃,但是这个层面上的数据丢弃无法补救。

建议vm-insert不要配置-dropSamplesOnOverload选项

vm-insert的http remote write协议层面

作为http服务器,可以看看vm-insert在http协议上如何处理过载:

see: lib/writeconcurrencylimiter/concurrencylimiter.go:32

// Do calls f with the limited concurrency.
func Do(f func() error) error {
// Limit the number of conurrent f calls in order to prevent from excess
// memory usage and CPU thrashing.
select {
case ch <- struct{}{}: // 放入channel成功代表允许的并发数足够
err := f() // 执行具体的协议回调函数
<-ch
return err
default:
} // All the workers are busy.
// Sleep for up to *maxQueueDuration.
concurrencyLimitReached.Inc()
t := timerpool.Get(*maxQueueDuration) // maxQueueDuration 默认60秒,也就是无法处理的请求最长要等待60秒
select {
case ch <- struct{}{}:
timerpool.Put(t)
err := f()
<-ch
return err
case <-t.C:
timerpool.Put(t)
concurrencyLimitTimeout.Inc()
return &httpserver.ErrorWithStatusCode{
Err: fmt.Errorf("cannot handle more than %d concurrent inserts during %s; possible solutions: "+
"increase `-insert.maxQueueDuration`, increase `-maxConcurrentInserts`, increase server capacity", *maxConcurrentInserts, *maxQueueDuration),
StatusCode: http.StatusServiceUnavailable, // 等待60秒后仍然没有资源,则向调用端返回http 503错误
}
}
}

由源码可知:后端过载后,发送数据变慢,变慢导致用于处理并发的协程长时间阻塞。新来的请求没有协程去处理,就进入了等待。等待足够长的时间仍然没有资源,就向调用端返回http 503错误码。

把无法处理的请求通过错误码返回,这样调用方就能够感知到后端的过载了。

通过metric可以查询到vm-insert上拒绝的请求数:vm_http_request_errors_total{protocol="promremotewrite"}

到这里,怎么样才能感知到,vm-insert到vm-storage这条写入链路上已经发生了过载?这个问题已经有了答案:

1.关闭-dropSamplesOnOverload选项,不要在vm-insert这个层面上丢包;

2.通过http remote write的状态码 http 503 来感知后端是否已经过载;

vm-insert仍然有其他细节可能导致丢包,比如配置了 -maxInsertRequestSize / -maxLabelValueLen / -maxLabelsPerTimeseries 等选项的情况下。

可以通过下面的表达式来查询vm-insert这个环节的转发成功率:

sum by () (rate(vm_rpc_rows_sent_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.*"})) /
sum by () (rate(vm_rows_inserted_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.*",type="promremotewrite"}))

如何合理配置vm-insert的参数

首先,分析一下vm-insert写数据到vm-storage的延迟是多少:

max by () (rate(vm_rpc_send_duration_seconds_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.*"}[1m]))

我实验群集查询出来的数值是 0.996~1.01秒。

-maxConcurrentInserts 参数不配置的情况下,每核的默认并发为4.

因此,每协程的延迟大约是: 1000ms / 4核 / 4 协程 = 62.5ms

vm-insert与vm-storage之间的延迟如此之短,所以 -insert.maxQueueDuration=60s 这个时间实在太长了。我在压测中发现,-insert.maxQueueDuration=2s能够带来很好的吞吐量,并使vm-insert的内存压力很小。

-insert.maxQueueDuration=2s后,remote write客户端的请求延迟要略大于2s。这样的话,vm写入链路上如果发生过载,绝大多数的过载请求都会通过 http 503来体现。

总结一下:

  • 通过metric max by () (rate(vm_rpc_send_duration_seconds_total{tenant=~"$tenant",namespace=~"$namespace",env_name=~"$env_name",pod_name=~"${cluster}.*"}[1m])) 来计算实例的最大延迟。然后除以总的协程数,得到每个协程的延迟。
  • 队列中的等待时间insert.maxQueueDuration要与上面得到的延迟值相对等同。否则,过载情况下会积累非常多远超于延迟时间的请求,容易导致vm-insert发生OOM
  • vm-insert的参数-maxConcurrentInserts建议不配置,使用默认的每核4协程的并发量。
    • 协程数增加对于vm-insert的吞吐量没有明显提升,反而导致增加协程调度的消耗。
    • 实际压测中发现每核4协程的吞吐量很好,瓶颈还是在vm-storage一侧。
  • 关闭-dropSamplesOnOverload,把过载的信息传给上游。然后由上游来选择丢弃还是重试。

3.过载的一系列问题

如果写入链路发生过载,是否会丢数据?如果丢数据,究竟在哪个环节丢数据?过载了如何做保护,如何避免雪崩?

综合上面的信息:

  • 如果不在vm-insert上配置-dropSamplesOnOverload选项,几乎不会发生丢数据

    • 如果需要知道在某些数据合法性检查的环节是否有丢数据,可以通过vm中的某些metric计算出来具体丢了多少;(压测过程中一次也没发生过)
  • 丢数据的最大环节仍然在vm-insert上,导致丢弃的原因是netstorage客户端的buffer写满。
  • 过载保护最好的位置是在remote write的发送端,通过http 503错误码来感知后端是否过载。
    • vm-storage过载后,表现为CPU耗满,插入时没有对应的可调度协程。最终导致vm-insert端连接超时。也就是说,vm-storage自身不会因为写入太多而发生崩溃。
    • vm-insert的过载一般由于vm-storage变慢导致。过载后,等待处理的缓冲区的数据变多,没有可用的insert协程,最终触发超时,返回http 503错误。只要insert.maxQueueDuration参数设置合理,一般也不会发生vm-insert上的崩溃。

总结

  • 把vm-insert和vm-storage看成一个整体,在这条路径上可以配置为不丢弃数据;
  • 把过载的检查和过载后的等待/重试等任务交给remote write的客户端;
  • vm-storage的单核性能极为强悍,且能够做到水平扩容,可以认为其数据写入能力是没有上限的。

vm-insert到vm-storage链路上的配置说明的更多相关文章

  1. Linux虚拟内存(swap)调优篇-“swappiness”,“vm.dirty_background_ratio”和“vm.dirty_ratio”

      Linux虚拟内存(swap)调优篇-“swappiness”,“vm.dirty_background_ratio”和“vm.dirty_ratio” 作者:尹正杰 版权声明:原创作品,谢绝转载 ...

  2. centos6 下FastDFS 在storage节点上nginx的fastdfs-nginx-module 模块编译出现的问题

    centos6.6  下FastDFS  在storage节点上   make(编译)nginx的fastdfs-nginx-module 出现如下报错: /root/fastdfs-nginx-mo ...

  3. 攻击链路识别——CAPEC(共享攻击模式的公共标准)、MAEC(恶意软件行为特征)和ATT&CK(APT攻击链路上的子场景非常细)

    结合知识图谱对网络威胁建模分析,并兼容MITRE组织的CAPEC(共享攻击模式的公共标准).MAEC和ATT&CK(APT攻击链路上的子场景非常细)等模型的接入,并从情报中提取关键信息对知识图 ...

  4. SQL VM上磁盘延迟高, 但Host和Storage Array上的延迟却很低的问题

    按照下面的步骤, 问题解决. =========================== Per Microsoft DDK, Microsoft storport.sys maintains a dev ...

  5. 虚拟机最佳实践:单个 VM、临时存储和已上传磁盘

    大家好! 我是 Drew McDaniel,来自 Microsoft Azure虚拟机功能研发团队,我从团队成立之初就已加入. 在本博客文章中,我将分享一些最佳实践指南,帮助您充分利用您的Azure虚 ...

  6. vm.dirty_background_ratio and vm.dirty_ratio

    http://hellojava.info/?p=264&utm_source=tuicool&utm_medium=referral 解决磁盘io紧张的一种临时方法 有些时候可能会碰 ...

  7. Standard 1.1.x VM与Standard VM的区别

    在Eclipse或MyEclipse中要设置Installed JREs时,有三个选择: - Execution Environment Description - Standard 1.1.x VM ...

  8. Azure Storage 分块上传

    概述 Azure 存储提供三种类型的 Blob:块 Blob.页 Blob 和追加 Blob.其中,块 Blob 特别适用于存储短的文本或二进制文件,例如文档和媒体文件. 块 Blob 由块组成,每个 ...

  9. Java HotSpot(TM) Client VM 与 server VM 的配置

    在Linux 6.5 下安装Elasticsearch 出现错误: JVM is using the client VM [Java HotSpot(TM) Client VM] but should ...

  10. 1, vm: PropTypes.instanceOf(VM).isRequired

    子模块的文件引入父工程对象时,出现红色warning,提示传入的对象类型不是所要求的类型. 思路是父工程引用的JS包和子模块使用的包不是同一个包,解决办法是父工程和子工程都使用同一个包. resolv ...

随机推荐

  1. Spring中部署Activiti流程定义的三种姿势

    摘要:本文对工作流Activiti框架中流程定义的部署进行了详细说明介绍. 本文分享自华为云社区<项目中工作流部署详细解析!Spring中部署Activiti流程定义的三种姿势>,作者:攻 ...

  2. 火山引擎DataTester智能发布:助力产品降低功能迭代风险

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群   对企业而言,应用大规模AB实验,可以帮助企业提高决策效率.降低试错成本,而近期火山引擎AB测试 DataTes ...

  3. 火山引擎DataLeap如何解决SLA治理难题(二):申报签署流程与复盘详解

    申报签署流程详解 火山引擎DataLeap SLA保障的前提是先达成SLA协议.在SLA保障平台中,以申报单签署的形式达成SLA协议.平台核心特点是优化了SLA达成的流程,先通过"系统卡点计 ...

  4. SpringBoot 拦截器 统一日志 记录用户请求返回日志

    你请求我接口,传了什么参数,我返回了什么值给你,全部记下来.防止扯皮 需求:记录每次用户请求Controller的Body参数, 思路:在每个Controller 该当中记录,容易漏记,如果在拦截器里 ...

  5. Mongodb--索引(转载)

    原文转载自:https://www.cnblogs.com/wyy1234/p/11032163.html 1 mongoDB索引的管理 本节介绍mongoDB中的索引,熟悉mysql/sqlserv ...

  6. 关于ABAP索引

    1.什么是索引 如果把数据库表看做一本书,索引就可以看做书的检索目录.目录中包含书中的大小标题(部分字段数据),并且有对应的数据表条目的页码(指针),可以快速的访问数据库表中对应行的所有字段内容 一个 ...

  7. Windows | 安装 Docker 遇到的 WSL 2 installation is incomplete 报错的解决方案

    控制面板中打开 Windows功能,在其中勾选 适用于 Linux 的 Windows 子系统 下载 WSL 更新包(非最新版本的也会报错) 更新包下载链接:https://wslstorestora ...

  8. Canal 组件简介与 vivo 帐号实践

    互联网应用随着业务的发展,部分单表数据体量越来越大,应对服务性能与稳定的考虑,有做分库分表.数据迁移的需要,本文介绍了vivo帐号应对以上需求的实践. 一.前言 Canal 是阿里巴巴开源项目,关于什 ...

  9. mysql和redis库存扣减和优化

    前言 大流量情况下的库存是老生常谈的问题了,在这里我整理一下mysql和redis应对扣除库存的方案,采用jmeter进行压测. JMETER设置 库存初始值50,线程数量1000个,1秒以内启动全部 ...

  10. 智慧地产-售楼中心 3D 沙盘可视化

    前言 随着"互联网+房地产"走入全国各大地产项目,房企依托互联网将房地产从传统地产转向智慧地产已然是眼下用户最欢迎的转型模式.智慧地产是由智慧社区.智慧园区.智慧公寓及智能家居等组 ...