问题现象
长期使用的 OceanBase 数据库 V2.2.3 版本的集群,有 10 台左右的机器监控指标cpu_usage 不准确,并触发了一系列运维问题。
以其中几台为例,单机的物理 CPU 使用率很低,只有 3-5C 左右。

但是监控项 cpu_usage 非常高,大概有 30C 左右。

经过排查发现对应租户的 dump tenant info 日志中,大查询挂起线程数 lq_waiting_workers_.size() 的数值异常,只有 -123,这个数值预期应该是 >=0(大于等于零)的。

而租户的调度巡检要保证租户的活跃线程数 token_cnt 恒定,因此会计算出需要分配的总线程数 workers = token_cnt + lq_waiting_workers_.size(),正常情况下 lq_waiting_workers_.size() >=0,所以总线程数 workers 一般会 >= token_cnt,但 lq_waiting_workers_.size() 数值异常为负,这就导致 workers 分配的明显偏少,预期要有至少 154 个,实际只有 31 个。
造成此次影响的是 cpu_usage 的不准。这是因为 cpu_usage 是通过 token_cnt 来计算的,因为异常导致的理论 token_cnt 比实际分配出来的线程数 workers 偏大,计算的 cpu_usage 也会比实际的使用率偏大。另外 lq_waiting_workers_.size() 异常还有可能导致租户线程数不够从而处理负载的能力不够,出现队列积压等现象。
关键诊断信息
触发条件
线程被判定为处理大查询请求而挂起后,达到 wait_timeout 触发自身唤醒。
出现概率一方面取决于场景大查询的数目,另一方面取决于大查询阈值 large_query_threshold 和 wait_timeout 数值,large_query_threshold 越小,wait_timeout 越小,这个问题概率就会更大,这两个值在各个版本默认值如下。
| 版本 | large_query_threshold | wait_timeout |
|---|---|---|
| V2.2.6 及老版本 | 100ms | query_timeout |
| V2.2.77 及新版本 | 5s | 60s |
并且这个要累积到一定的次数后才能产生可感知的影响。前面所述长期使用的 OceanBase 数据库 V2.2.3 版本的集群,有 10 台左右的机器监控指标cpu_usage 不准确的场景,lq_waiting_workers_.size() 的数值可能在很早出现异常了,逐渐累积,造成租户工作线程数明显不足,cpu usage 统计不准确等。
事前巡检
将 large_query_threshold 设置的尽可能大,能减小问题发生概率。
事后诊断
查询 dump_tenant_info,其中的 lq_waiting_workers_.size() 字段为负数。

问题原因
老代码缺陷。大查询挂起线程的唤醒有一个并发问题,会导致 lq_waiting_workers_.size() 重复减。
具体的,挂起唤醒有两条路径,其一是常规路径,由其他线程执行一小段调度逻辑来唤醒。

另一条路径非常规,是被挂起的线程自身 wait timeout 后唤醒自己。

这里是先判定 node->next 是否为 null,后加锁。但有可能在加锁前 node->next 被置为 null,导致重复 remove。
观察队列本身的实现,在 unlink 前只对当前的 node 判空,没有对 node->next 判空

这会导致后面的 size_-- 被重复执行。
因此在被挂起的线程 wait timeout 后可能会导致 lq_waiting_workers.size() 被重复减,累积一定次数之后可能就会变为负值。
问题的风险及影响
可能导致租户工作线程数不足,租户没有足够的处理负载的能力,导致队列积压等现象发生。
可能导致租户
cpu_usage计算不准确,数值持续性的明显偏大。
影响租户
影响 OceanBase 数据库中的 SYS 租户和 Oracle 租户以及 MySQL 租户。
影响版本
OceanBase 数据库企业版 V2.2.77 GA(oceanbase-2.2.77-20210508211731)及之后版本、V3.1.2 GA(oceanbase-3.1.2-20210618150922)及之后版本、V3.2.2 GA(oceanbase-3.2.2-20211130225726)及之后版本、V3.2.3 GA(oceanbase-3.2.3.0-20220418212020)及之后版本。
解决方法及规避方式
解决方法:
升级至问题已修复版本。目前已修复的版本包括 OceanBase 数据库企业版 V3.2.3 BP3(oceanbase-3.2.3.0-20220622171245)。
如果因为线程数不够而明显的影响了租户负载能力造成队列积压,或者
cpu_usage不准确影响了运维和监控,可以通过重启 OBServer 恢复。
规避方式:
尽量设置比较大的大查询阈值
large_query_threshold,可减小问题发生的概率。尽量不触发大查询请求。