TT Lab
开始
学习 学习路径 课程

分布式链路断掉的地方

留下什么,丢弃什么

在 TT Lab 中继续学习

目标

把跟踪全部存下来,成本承受不起;随机地减少,又会让真正想看的请求消失。本实验用数字来确定这二者之间的取舍。

材料

/opt/lab/trace/spans.jsonl   요청 1만 건 (지속 시간·상태·서비스)
mkdir -p /root/trace && cp /opt/lab/trace/* /root/trace/ && cd /root/trace
head -2 spans.jsonl

需要留下的文件

01-shape.txt    분포와 오류 건수
02-head.txt     무작위 1% 가 남기는 것
03-tail.txt     오류·느린 것을 전부 남길 때
04-cost.txt     저장 비용
05-memory.txt   꼬리 표본의 숨은 비용
06-decide.md    우리가 쓸 규칙
07-notes.md     왜 그런지

先看分布

求出 spans.jsonl 的总条数、p50、p95、p99 和错误条数,保存到 01-shape.txt,并用一句话写出这个分布的形状。

python3 - <<'PY'
import json
d = sorted(json.loads(l)['duration_ms'] for l in open('spans.jsonl'))
n = len(d)
print(n, d[n//2], d[int(n*0.95)], d[int(n*0.99)])
PY

对于这种分布,平均值什么也说明不了。它的形状是大多数请求很快、少数请求极慢,所以平均值会落在两者之间一个谁也没有经历过的值上。

随机 1% 会漏掉什么

从全部请求中随机选取 1%,把这份样本中保留下来的超过 p99 的请求和错误各有多少个保存到 02-head.txt。写出随机样本漏掉了什么。

import random; random.seed(7)
sample = random.sample(rows, len(rows)//100)

会留下 100 条左右,其中超过 p99 的大概只有一两个。

故障调查需要的恰恰就是这一两个。随机样本能很好地留下常见的,却会丢掉罕见的——而我们想看的永远是罕见的那一类。

结束之后再挑选

按“错误全部保留、超过 p99 的也全部保留、其余保留 1%”来挑选,把保留的条数保存到 03-tail.txt,并写出它是随机 1% 的多少倍。

因为是在请求结束之后才作出判断,所以称为尾部采样(tail sampling)。

把 129 条错误、超过 p99 的(非错误)请求,以及其余请求的 1% 加起来,会得到 300 条左右——大约是随机 1% 的三倍,但想看的一个都没有丢。

采样比例就是成本

假设一条跟踪为 4KB,以每秒 1,000 个请求、保留 30 天为基准,计算 (1) 全部存储、(2) 随机 1%、(3) 尾部采样的存储量,并保存到 04-cost.txt。

公式是:每秒请求数 × 86,400 × 保留天数 × 单条大小 × 采样比例。

全部存储的话,一天就是个很大的数字。如果不先做这个计算,一上来就说“先全部打开”,下个月看到账单时才会明白。

尾部采样的隐性成本

估算要做尾部采样,Collector 必须把什么保存多久,以及此时需要多少内存,并保存到 05-memory.txt。

请求结束之后才能知道它是慢还是出错。所以 Collector 必须把该请求的所有跨度保存在内存中,直到结束。

所需内存 ≈ 每秒请求数 × 平均耗时(秒)× 单条大小。

这个数字大的时候,需要把 Collector 拆成多台,而那时同一条跟踪的跨度必须发往同一个 Collector,才能作出判断。这正是尾部采样运维变得困难的地方。

把它写成规则

在 06-decide.md 中写出我们的服务要使用的采样规则。用数字确定如何处理错误、慢请求的阈值、其余请求的比例,并写明各自的理由。

“适当地采样”不是规则。下一个人能原样把它搬进配置里,才算规则。

还要确定阈值是取 p99,还是取固定值(例如 1 秒)。p99 会随着服务变慢而一同升高,所以在变慢的过程中,可能什么也捕获不到。

写给下一个阅读的人

从这里看到的内容中挑选至少四点,整理到 07-notes.md 中。不要写做了什么,而要写为什么会这样。

设想一下,是将来重新调整采样配置时的你自己在读这份笔记。“选择了尾部采样”没有帮助,而“随机采样能很好地留下常见的却会丢掉罕见的,我们想看的永远是罕见的那一类”则有帮助。