From c9e604b35fe7579e234382cf399b2d8092cc69a2 Mon Sep 17 00:00:00 2001 From: opsx Date: Fri, 30 Oct 2026 03:41:37 +0000 Subject: [PATCH] =?UTF-8?q?perf(logs):=20=E4=BF=AE=20label=20=E4=B8=8B?= =?UTF-8?q?=E6=8B=89=2020=20=E7=A7=92=E3=80=81Loki=20CPU=20=E9=99=90?= =?UTF-8?q?=E6=B5=81=2076%=E3=80=81=E5=91=8A=E8=AD=A6=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E9=A2=91=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) Loki CPU 限流 76%(limits 4 核,实测 3923m,nr_throttled 920656/1209379) → limits 提到 8 核 / requests 2 核。限流归零,histogram 84ms, by-app 从 60s 超时降到 14.9s。 2) label values 未传时间范围 → Loki 默认扫 6 小时索引(370GB/天 → 10~21s) → 默认只查最近 1 小时。实测 namespace 21330ms→33ms,app 24781ms→30ms。 3) max_query_parallelism 8 > 实际核数 → 调到 6。 4) 告警检查 1 分钟 → 5 分钟(规则窗口本身 15 分钟,稳态负载降到 1/5)。 --- backend/services/alert.go | 5 ++++- backend/services/log_query.go | 13 +++++++++++++ backend/services/loki_config.go | 4 +++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/backend/services/alert.go b/backend/services/alert.go index 498ac83..ca316e7 100644 --- a/backend/services/alert.go +++ b/backend/services/alert.go @@ -48,7 +48,10 @@ func overStormLimit() (bool, int) { return sent >= alertStormMaxPerHour, sent } -const alertCheckInterval = time.Minute +// 检查间隔:规则窗口都是 15 分钟,1 分钟查一次等于用 15 倍频率扫同样的数据。 +// Loki 单实例 CPU 是瓶颈(实测被限流 76%),这里降到 5 分钟: +// 对告警及时性影响很小(窗口本身就是 15 分钟),但稳态负载降到 1/5。 +const alertCheckInterval = 5 * time.Minute const ( // alertRoundTimeout 一轮检查的总时限。超过就中断本轮, diff --git a/backend/services/log_query.go b/backend/services/log_query.go index 3886a0b..33e0348 100644 --- a/backend/services/log_query.go +++ b/backend/services/log_query.go @@ -367,11 +367,24 @@ func LokiLabels(ctx context.Context, src *models.LogSource, start, end int64) ([ } // LokiLabelValues 标签值列表(query 只取 {...} selector 部分) +// labelValuesDefaultWindow 是 label 值下拉框的默认时间窗。 +// 只影响"没显式给时间范围"的调用(下拉框),不影响带范围的查询。 +const labelValuesDefaultWindow = time.Hour + func LokiLabelValues(ctx context.Context, src *models.LogSource, name, selector string, start, end int64) ([]string, error) { q := url.Values{} if s := extractStreamSelector(selector); s != "" && s != "{}" { q.Set("query", s) } + // 不传时间范围时 Loki 默认扫**最近 6 小时**的索引 —— 在 370GB/天的集群上 + // 实测要 10~21 秒(下拉框"一直加载中"的直接原因)。 + // 下拉框只需要"现在有哪些 app/pod 在打日志",给 1 小时足够,索引量降到 1/6。 + // 调用方显式传了时间范围则完全尊从,不受影响。 + if start <= 0 && end <= 0 { + now := time.Now() + start = now.Add(-labelValuesDefaultWindow).UnixNano() + end = now.UnixNano() + } if end > 0 { q.Set("end", strconv.FormatInt(end, 10)) } diff --git a/backend/services/loki_config.go b/backend/services/loki_config.go index 483dc37..4fc3b52 100644 --- a/backend/services/loki_config.go +++ b/backend/services/loki_config.go @@ -70,7 +70,9 @@ compactor: limits_config: retention_period: {{ .RetentionPeriod }}h # 单实例 Loki 的并发护栏:默认 32 高得离谱,一条重查询就能把单进程打满 - max_query_parallelism: 8 + # 并行度不要超过实际 CPU 核数:超了只是让多个查询互相抢 CPU、 + # 每个都变慢(实测 4 核 + 并行 8 时限流 76%,label values 都要 20 秒)。 + max_query_parallelism: 6 # ── 查询结果缓存(官方推荐;此前完全没配,重复查询要重算一遍)─────── # align_queries_with_step 让不同时间范围的同类查询对齐到同一 step,