问题背景
集群版本 :v5.4.3
问题现象:集群 cpu 资源使用率飙高,集群整体 duration 上升,造成部分业务收到影响
问题处理
1. sql 定位
根据 dashboard top sql ,以及慢查询 定位到相关 sql ,为 120亿 表全表扫描
2. 临时解决
sql 相关查询条件在表中是有索引的,临时通过 sql binding 的方式 绑定走索引的执行计划,集群资源使用率恢复正常。
3. 查询条件有对应索引为什么要走全表?
注: 查看时间点 7 月 8日 11 时左右
- 查看表元信息,可以看到表是一直在更新的,最后更新时间是 2024-07-08 row count 120亿左右
- 查看索引统计信息,发现索引统计信息缺失,最后更新时间为 6月13日。 经询问此集群为迁移升级的目标集群,6月13日 为数据导入时间,也就是从数据进到集群之后统计信息就没有正常收集过。
到这里其实 执行计划 错误的原因已经找到 : 相关表统计信息缺失导致优化器无法正确预估 cost ,进而导致执行计划选择错误,走到了全表扫描,但是正常情况下集群是有自动统计信息收集的,为什么索引的统计信息一直没有收集成功呢?
那么尝试手动收集统计信息呢试试呢?手动收集没有成功,报错如下:
mysql> analyze table content_xxx_xxx index content_index;
ERROR 1105 (HY000): other error: encoding failed
根据如上报错基本可以判断 统计信息收集失败的原因是表中存在非法字符导致。
此时最直接的解决办法是重新将数据重新导出导入,并指定字符集,但是这张表数据量有 120 亿,这个操作动作太大。且业务没有反馈数据查询存在问题,说明有异常数据的部分占比较小,那下一步就是通过日志信息尝试定位具体异常数据范围。
4. 查看 tikv 、 tidb 日志 定位 analyze 异常信息:
07/08 11:07:37 tidb 日志显示 content 表统计信息收集失败
查看对应时间点 tikv 日志,定位到报错的表的 region 信息,报错 region id 为 799953
5. 通过 region id 799953 查询 region 详细信息
得到 startkey : 7480000000000000FF715F72800000003CFF6835E20000000000FA
endkey: 7480000000000000FF715F72800000003CFFA61F7F0000000000FA
region 799953
{
"id": 799953,
"start_key": "7480000000000000FF715F72800000003CFF6835E20000000000FA",
"end_key": "7480000000000000FF715F72800000003CFFA61F7F0000000000FA",
"epoch": {
"conf_ver": 155,
"version": 104105
},
"peers": [
{
"id": 800344,
"store_id": 11,
"role_name": "Voter"
},
{
"id": 800343,
"store_id": 20,
"role_name": "Voter"
},
{
"id": 1465083,
"store_id": 6,
"role_name": "Voter"
},
{
"id": 1960146,
"store_id": 1148820,
"role_name": "Voter"
},
{
"id": 3532222,
"store_id": 1147431,
"role_name": "Voter"
}
],
"leader": {
"id": 800344,
"store_id": 11,
"role_name": "Voter"
},
"written_bytes": 0,
"read_bytes": 135,
"written_keys": 0,
"read_keys": 3,
"approximate_size": 86,
"approximate_keys": 107417
}
/ # ./tikv-ctl --host 127.0.0.1:20160 raft region -r 799953
{
"region_infos": {
"799953": {
"raft_apply_state": {
"applied_index": 24971,
"commit_index": 24971,
"commit_term": 7,
"truncated_state": {
"index": 24969,
"term": 7
}
},
"raft_local_state": {
"hard_state": {
"commit": 24971,
"term": 7,
"vote": 800344
},
"last_index": 24971
},
"region_id": 799953,
"region_local_state": {
"region": {
"end_key": "7480000000000000FF715F72800000003CFFA61F7F0000000000FA",
"id": 799953,
"peers": [
{
"id": 800344,
"role": "Voter",
"store_id": 11
},
{
"id": 800343,
"role": "Voter",
"store_id": 20
},
{
"id": 1465083,
"role": "Voter",
"store_id": 6
},
{
"id": 1960146,
"role": "Voter",
"store_id": 1148820
},
{
"id": 3532222,
"role": "Voter",
"store_id": 1147431
}
],
"region_epoch": {
"conf_ver": 155,
"version": 104105
},
"start_key": "7480000000000000FF715F72800000003CFF6835E20000000000FA"
}
}
}
}
}
6. 根据 startkey 、 endkey 定位表中存在问题的数据范围
解析出来的数据范围为 1013462498 ~ 1017519999
MySQL [(none)]> SELECT TIDB_DECODE_KEY('7480000000000000FF715F72800000003CFF6835E20000000000FA');
+---------------------------------------------------------------------------+
| TIDB_DECODE_KEY('7480000000000000FF715F72800000003CFF6835E20000000000FA') |
+---------------------------------------------------------------------------+
| {"_tidb_rowid":1013462498,"table_id":"113"} |
+---------------------------------------------------------------------------+
MySQL [(none)]> SELECT TIDB_DECODE_KEY('7480000000000000FF715F72800000003CFFA61F7F0000000000FA');
+---------------------------------------------------------------------------+
| TIDB_DECODE_KEY('7480000000000000FF715F72800000003CFFA61F7F0000000000FA') |
+---------------------------------------------------------------------------+
| {"_tidb_rowid":1017519999,"table_id":"113"} |
+---------------------------------------------------------------------------+
7. 根据上文解析出的 数据范围查询表中相关数据
- 可能存在异常的数据量为 109574 条
mysql> select count(*) from content_xxxx_xxxx t where id>=1013462498 and id<=1017519999;
+----------+
| count(*) |
+----------+
| 109574 |
+----------+
1 row in set (0.07 sec)
- 尝试通过 char_length() 函数定位异常数据,异常数据通过函数处理时会报错,以此来确定异常数据范围。
mysql> select count(*) from content_xxxx_xxxxxx t where id>=1013462498 and id<=1017519999 and char_length(content_id) > 0;
+----------+
| count(*) |
+----------+
| 109574 |
+----------+
1 row in set (0.33 sec)
mysql> select count(*) from content_xxxxxx_xxxxxx t where id>=1013462498 and id<=1017519999 and char_length(msg) > 0;
+----------+
| count(*) |
+----------+
| 66262 |
+----------+
1 row in set (0.52 sec)
mysql> select count(*) from content_xxxxxxx_xxxxxx t where id>=1013462498 and id<=1017519999 and char_length(error_msg) > 0;
ERROR 1105 (HY000): invalid input value: Utf8Error { valid_up_to: 183, error_len: Some(1) }
经确认, 只有 char_length(error_msg) 作为筛选条件的时候会报错,说明异常数据存在于 error_msg 字段。
- 确认异常字段后,在通过对 id 1013462498 ~ 1017519999 的范围进行二分法查找,定位具体出错的行,进一步通过业务手段处理,或者针对性地对该段数据进行重新导入。
总结与建议:
-
此次故障版本为 v5.4.3 ,新版本对非法数据的校验和检查制度更为完善,升级到 v6.5+ 以上的新版本可以获得更好的使用体验。
-
数据迁移如何避免非法数据。
- 参数: tidb_skip_utf8_check 建议关闭此参数,避免跳过集群本身的 utf8-check
- 如使用 lightning 工具导入,注意 character-set 可手动指定为需要的字符集,避免自动识别。更多请参考链接:https://docs.pingcap.com/zh/tidb/stable/tidb-lightning-configuration
- 数据迁移之后检查恢复日志是否存在异常。
- 数据迁移到新集群后,第一时间收集统计信息,并检查统计信息收集状态。