Loading... # 一、背景 同事发来一个图片,说是日志显示,一个服务推送数据到opentsdb得到了http 500返回码。 经过排查,确实如此,而且还不少。 ![2024-03-27T06:47:16.png][1] # 二、现象 排查现象,有波动,怀疑是跟并发量有关系。 但是,查询昨日正常与异常数据,前者2万左右,后者几百。不像是并发量问题。 # 三、猜测 ## (1)内存问题 之前CDH集群报内存不足,赶紧进行了内存扩容。 但是,问题依旧。 ![2024-03-27T06:47:40.png][2] # 四、定位 研发仔细分析日志,并通过本地调试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} ``` ![2024-04-19T03:20:00.png][3] * 删除\x00的tagv `\x00`的tagv消失 ``` deleteall 'tsdb-uid',"\x00",'id:tagv' scan 'tsdb-uid', {LIMIT => 3, REVERSED => false} ``` ![2024-04-19T03:20:43.png][4] * 新建一个tagv用于打数据。出现了\x00的tagv ![2024-04-19T03:21:36.png][5] 但是写入数据错误 (should never happen!!!)。估计是新数据需要新增tagv uid的时候,当前tsdb-uid表中tagv uid已经分配了出去。所以本次分配失败,返回给客户端500。 下次客户端继续发送put请求,继续请求分配tagv,tsdb继续将tagv的uid增长+1。 ![2024-04-19T03:21:57.png][6] * 写一个脚本,一直写数据 虽然仍然报“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```应该就可以了 。 ![2024-04-19T03:23:04.png][7] * 到了D4M的下一个D4N,数值停止了。 ![2024-04-19T03:23:14.png][8] * we are in ! ![2024-04-19T03:23:25.png][9] * 开始正常写入数据 ![2024-04-19T03:23:38.png][10] ## (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多万行,第一个有问题的数据的位置。 ![2024-04-19T03:29:09.png][11] * 找到这行数据 ``` └─$ 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!' ``` ![2024-04-19T03:29:55.png][12] 然后放到线上去执行。 * 删除\x00 tagv的值 当研发把异常代码注释掉之后,开始删除\x00。随着新数据metric+tags到达,将申请uid,然后促使\x00的tagv value开始递增变化。 ``` hbase shell deleteall 'tsdb-uid',"\x00",'id:tagv' scan 'tsdb-uid', {LIMIT => 3, REVERSED => false} ``` * 第二天一大早查看删除情况 ![2024-04-19T03:30:55.png][13] * 查看uid情况 ![2024-04-19T03:31:04.png][14] * 模拟数据打入,居然成功了。 可能是opentsdb内部机制,实现了uid回收。 ![2024-04-19T03:31:24.png][15] ## (4)剩余的异常uid删除 现在1600多万的异常uid还在以每天60左右的速度删除。当删除之后,基本上opentsdb数据库中基于tagv的value异常值将得到极大缓解。 [1]: https://www.sddts.cn/usr/uploads/2024/03/564866468.png [2]: https://www.sddts.cn/usr/uploads/2024/03/1893805852.png [3]: https://www.sddts.cn/usr/uploads/2024/04/241514088.png [4]: https://www.sddts.cn/usr/uploads/2024/04/87090478.png [5]: https://www.sddts.cn/usr/uploads/2024/04/95384012.png [6]: https://www.sddts.cn/usr/uploads/2024/04/479063204.png [7]: https://www.sddts.cn/usr/uploads/2024/04/2536141011.png [8]: https://www.sddts.cn/usr/uploads/2024/04/3317020.png [9]: https://www.sddts.cn/usr/uploads/2024/04/4206219912.png [10]: https://www.sddts.cn/usr/uploads/2024/04/3541019213.png [11]: https://www.sddts.cn/usr/uploads/2024/04/2245246043.png [12]: https://www.sddts.cn/usr/uploads/2024/04/702703358.png [13]: https://www.sddts.cn/usr/uploads/2024/04/2440917229.png [14]: https://www.sddts.cn/usr/uploads/2024/04/1430841710.png [15]: https://www.sddts.cn/usr/uploads/2024/04/433997141.png 最后修改:2024 年 05 月 11 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏