一、背景
同事发来一个图片,说是日志显示,一个服务推送数据到opentsdb得到了http 500返回码。
经过排查,确实如此,而且还不少。
二、现象
排查现象,有波动,怀疑是跟并发量有关系。
但是,查询昨日正常与异常数据,前者2万左右,后者几百。不像是并发量问题。
三、猜测
(1)内存问题
之前CDH集群报内存不足,赶紧进行了内存扩容。
但是,问题依旧。
四、定位
研发仔细分析日志,并通过本地调试opentsdb接口,发现是某一个id出现了问题。遇到了opentsdb的三字节问题。
- 什么是三字节问题
其实“三字节问题”是 OpenTSDB 中一个具体的实现细节。在 OpenTSDB 中,每一个唯一的时间序列都会有一个与之对应的 ID,这个 ID 是由一个特定的编码算法生成的,长度为 3 字节。因此,对于每一个唯一的度量/标签组合,OpenTSDB 只能生成最多 2^24(即约 1677 万)个不同的 ID。如果你的时间序列数量超过了这个上限,OpenTSDB 就无法再生成新的 ID,你将无法添加新的时间序列。 换句话说,OpenTSDB 的“三字节问题”指的是对每一个度量/标签的组合,OpenTSDB 只能处理最多约 1677 万个唯一的时间序列。这是一个极端的情况,一般不会出现。然而,对于一些大规模、长期的数据收集任务,可能会达到这个上限。 如果你遇到了这个问题,如我前面提到的,你可能需要清理一部分数据,或者调整你的数据存储策略和时间序列设计。在不改动 OpenTSDB 代码的情况下,这些可能是目前解决这个问题的可行方案。如果有更多疑问,可以继续提问。 - 如果删除了三年前的数据,那么ID会有么?
删除数据并不会直接释放其对应的时间序列 ID,因为 ID 一旦生成会被保留,以保证时间序列的唯一性。即使对应的数据点被删除,但在 OpenTSDB 中,这个 ID 依然会被保留,因此删除数据并不能解决时间序列 ID 耗尽的问题。 在 OpenTSDB 的设计中,没有直接的办法来重新生成或复用已使用过的时间序列 ID。这是为了保证每一个时间序列的唯一性和一致性。因此,“三字节问题”实际上是 OpenTSDB 的一个设计限制。 如果你遇到了这个问题,可能需要考虑采用其他策略进行优化,比如重新考虑和设计你的标签和度量的组织方式;或者利用 OpenTSDB 提供的数据预处理功能,将某些数据进行整合,从而减少时间序列的数量。 这是一个复杂的问题,需要根据你具体的应用场景和需求来进行解决。如果有更多的疑问或需要更深入的讨论,请随时提问。
五、解决(2024年4月19日)
当时间来到4月中旬,客户着急使用,可是已经没有了uid分配。
(1)测试环境测试
查看当前\x00的tagv的值
hbase shell scan 'tsdb-uid', {LIMIT => 3, REVERSED => false}
删除\x00的tagv
\x00
的tagv消失deleteall 'tsdb-uid',"\x00",'id:tagv' scan 'tsdb-uid', {LIMIT => 3, REVERSED => false}
- 新建一个tagv用于打数据。出现了\x00的tagv
但是写入数据错误 (should never happen!!!)。估计是新数据需要新增tagv uid的时候,当前tsdb-uid表中tagv uid已经分配了出去。所以本次分配失败,返回给客户端500。
下次客户端继续发送put请求,继续请求分配tagv,tsdb继续将tagv的uid增长+1。
- 写一个脚本,一直写数据
虽然仍然报“should never happen!!!”,但是tsdb-uid表中的tagv uid的值在一直增长。使用如下程序继续。这个程序会打到一个之前没有的uid,
#!/bin/bash
while true
do
current_time=$(date +%s)
current_time_ms=$((current_time * 1000))
current_second=$(date +%S)
data=$(cat <<EOF
[
{
"metric": "jacky",
"value": "$current_second",
"timestamp": "$current_time_ms",
"tags": {
"dvid": "109",
"type": "1",
"did": "123456789012345609"
}
}
]
EOF
)
echo "Sending data to OpenTSDB..."
response=$(curl -X POST http://192.168.124.194:4242/api/put --header "Content-Type: application/json" -d "$data" -w "%{http_code}" -s)
status_code=$(echo "$response" | tail -n1)
response_body=$(echo "$response" | sed '$d')
if [[ "$status_code" -eq 200 || "$status_code" -eq 204 ]]; then
echo "Data sent successfully. Server responded with status code: $status_code"
else
echo "Failed to send data. Server responded with status code: $status_code"
echo "Response body: $response_body"
fi
sleep 1
done
- 随着上面程序的运行,tagv这个地方的value一直在增加。等到删除tagv
\x00
之前的值。也许当该值增加到之前的\0C\D4M
应该就可以了 。
- 到了D4M的下一个D4N,数值停止了。
- we are in !
- 开始正常写入数据
(2)处理线上
查看uid使用情况
[root@cdhslave3 ~]# tsdb uid grep tagv . | wc -l 16776889 [root@cdhslave3 ~]# tsdb uid grep metrics . | wc -l 334181 [root@cdhslave3 ~]# tsdb uid grep tagk . | wc -l 7
- 分析tagv 数量16776889的组成
# 大于等于4小于14字节的一共是16487127
data.tagv.less_than_14_bytes.txt 15488132
data.tagv.less_than_7_bytes.txt 998995
# 应该是正常的数量
data.tagv.less_than_4_bytes.txt 1000
data.tagv.非数字.txt 5445
data.tagv.more_than_14_bytes.txt 283317
- 分析出来tagv的值后,组成命令
tsdb uid delete tagv 2357129 ;echo deleted!;
tsdb uid delete tagv 23571293 ;echo deleted!;
tsdb uid delete tagv 23571305 ;echo deleted!;
tsdb uid delete tagv 2357131 ;echo deleted!;
tsdb uid delete tagv 23571311 ;echo deleted!;
tsdb uid delete tagv 2357132 ;echo deleted!;
tsdb uid delete tagv 23571329 ;echo deleted!;
tsdb uid delete tagv 23571337 ;echo deleted!;
tsdb uid delete tagv 235713378 ;echo deleted!;
tsdb uid delete tagv 23571344 ;echo deleted!;
- 因为命令比较长,所以实用split分离成15个文件,每个服务器跑3个。
(3)加急处理
按照每天60万的速度,删除1600多万的数据需要一个月左右。但是,用户等不及。
那就再想想办法,删除最近的10万数据先。
- 下载所有tsdb-uid表的数据。
cd /tmp
cat <<'EOF'>commands.txt
scan 'tsdb-uid', {REVERSED => false}
exit
EOF
hbase shell commands.txt > data.all.txt
- 分离出来tagv
cat data.all.txt | grep tagv > data.all.tagv.txt
- 刷新,找到大约30~40多万行,第一个有问题的数据的位置。
- 找到这行数据
└─$ cat data.all.tagv.txt| grep -n "value=319338$"
219835: \x03Z\xBD column=name:tagv, timestamp=1689656981443, value=319338
- 从上面这行开始,往后看10万行
sed -n '219835,319835p' data.all.tagv.txt | awk '{print $(NF)}' | awk -F\= '{print $2}' | sort -n > data.all.tagv.result.txt
- 修改成删除命令
vi data.all.tagv.result.txt
sed -i 's:^:tsdb uid delete tagv :g'
sed -i 's:$:;echo deleted!'
然后放到线上去执行。
- 删除\x00 tagv的值
当研发把异常代码注释掉之后,开始删除\x00。随着新数据metric+tags到达,将申请uid,然后促使\x00的tagv value开始递增变化。
hbase shell
deleteall 'tsdb-uid',"\x00",'id:tagv'
scan 'tsdb-uid', {LIMIT => 3, REVERSED => false}
- 第二天一大早查看删除情况
- 查看uid情况
- 模拟数据打入,居然成功了。
可能是opentsdb内部机制,实现了uid回收。
(4)剩余的异常uid删除
现在1600多万的异常uid还在以每天60左右的速度删除。当删除之后,基本上opentsdb数据库中基于tagv的value异常值将得到极大缓解。
5 条评论
delete all the useless tagv
tsdb uid delete tagv tagv-name
这样可以解决问题么?数据层面没有清理
没有试过,我这里不删除数据。
opentsdb不同版本有不同的命令或者特性,可以手动测试。
should never happen!!!这个地方没看懂啥意思呢
刚看到。忘记了。大概是不应该出现"\x00的tagv"
deleteall 'tsdb-uid',"\x00",'id:tagv'
这个操作完成后,会重新生成新的行? 不会有啥风险吧