2.40 dble中线程处理(print、kill、recover)
目前dble内部维护的主要以下几个线程:
mysql> select * from dble_thread_pool;
+----------------------+-----------+----------------+--------------+--------------------+
| name | pool_size | core_pool_size | active_count | waiting_task_count |
+----------------------+-----------+----------------+--------------+--------------------+
| Timer | 1 | 1 | 0 | 0 |
| TimerScheduler | 2 | 2 | 0 | 15 |
| frontWorker | 8 | 8 | 1 | 0 |
| managerFrontWorker | 4 | 4 | 4 | 0 |
| backendWorker | 8 | 8 | 0 | 0 |
| complexQueryWorker | 8 | 8 | 1 | 0 |
| writeToBackendWorker | 8 | 8 | 8 | 0 |
| NIOFrontRW | 8 | 8 | 8 | 0 |
| NIOBackendRW | 8 | 8 | 8 | 0 |
+----------------------+-----------+----------------+--------------+--------------------+
mysql> select * from dble_thread_pool_task;
+----------------------+-----------+-------------------+-----------------+----------------+------------+
| name | pool_size | active_task_count | task_queue_size | completed_task | total_task |
+----------------------+-----------+-------------------+-----------------+----------------+------------+
| Timer | 1 | 0 | 0 | 89208 | 89208 |
| TimerScheduler | 2 | 0 | 15 | 1158830 | 1158845 |
| frontWorker | 8 | 0 | 0 | 0 | 0 |
| managerFrontWorker | 4 | 0 | 0 | 60 | 60 |
| backendWorker | 8 | 0 | 0 | 11339 | 11339 |
| complexQueryWorker | 8 | 1 | 0 | 2189 | 2190 |
| writeToBackendWorker | 8 | 0 | 0 | 0 | 0 |
+----------------------+-----------+-------------------+-----------------+----------------+------------+
7 rows in set (0.42 sec)
补充(在非性能模式下):
- TimerScheduler,作为dble内部定时任务的调度器;
- 线程数固定(core_pool_size)2个,搭配无界队列;活动时机由定时任务触发周期而定,active_count有时为0;
- 定时任务个数见task_queue_size;有时task_queue_size会减少,说明部分任务正在执行中;
- 负责几个重要的定时任务包括心跳检测、主从延迟检测、疑似残留xaid检测。
- 如果此连接池中2个线程都hang了,completed_task不变,task_queue_size表示任务个数会持续增长
- Timer, 异步参与执行TimerScheduler线程中做一些较为复杂任务;
- 核心线程为1个,预备线程数1个,搭配有界队列(固定大小65535);pool_size初始时为1,当队列满时,pool_size会增大为2;
- 如果此连接池中2个线程都hang了,则pool_size、active_count均为2,completed_task、total_task不再变,task_queue_size值为65535。
- frontWorker,负责8066前端连接处理请求的线程池;
- 线程数固定,且为常驻线程(active_count等于pool_size),线程长期均处于RUNNING状态
- 如果active_count小于pool_size,可以尝试使用thread @@recover name='0-frontWorker';
- managerFrontWorker,负责9066前端连接处理请求的线程池;
- 同frontWorker
- writeToBackendWorker,广播下发SQL时候批量处理的线程池
- 同 frontWorker
- NIOFrontRW,进行前端网络IO吞吐的线程数
- 同frontWorker
- NIOBackendRW,进行后端网络IO吞吐的线程数
- 同frontWorker
- backendWorker,后端业务处理线程
- 线程数固定,但非常驻线程;业务需要处理时,active_count会+1,但不超过pool_size
- complexQueryWorker,负责处理一些复杂逻辑或者;
- 线程数不固定,且非常驻线程;业务需要处理时,active_count会+1,pool_size会出现超过core_pool_size
- 其他线程
- 只能通过
thread @@print
或者jstack等方式观测
- 只能通过
2.40.1 dble中观察疑似hang的线程
2.40.1.1 内部检测
局限:仅对Timer、TimerScheduler线程&线程池的检测
dble单独起一个内部观测的"ThreadChecker"线程,固定间隔2min检测一次。
检测机制:
- 若某个任务执行的时间长达10s -- thread.log中会有提示
Thread[{}] suspected hang, execute time:[{}ms] more than 10s, currentState:[{}]
- 若本次线程池距离上一次检测周期所保留的快照对比active_task_count、completed_task没有发生变化,则thread.log中会有提示
The thread pool where the thread[{}] is located is in the hang state and cannot work. Trigger alarm
,且会有DBLE_THREAD_SUSPECTED_HANG告警 - 若不满足第2点了,则DBLE_THREAD_SUSPECTED_HANG告警会自动解决
2.40.1.2 通过print线程堆客栈的方式,人为判断
thread @@print
打印所有堆栈信息thread @@print name=?
指定单个线程打印堆栈信息
强调注意:这两个命令类似于jstack工具,都会有STW的风险;因此建议不要轻易执行print命令
2.40.2 当确定某个线程执行任务的过长或者hang了,dble还可以哪些手段可以补救?
thread @@kill name=?
,用于中断某个线程正在执行任务thread @@kill poolname=?
,仅支持TimerScheduler/Timer线程池中断thread @@recover name=?
,一般对常驻线程的恢复操作thread @@recover poolname=?
,仅支持TimerScheduler/Timer线程池恢复
强调注意:不要随意执行以上命令
具体场景&尝试补救
- TimerScheduler
- 场景1:当发现存在单个线程hang了(多个时间点这个线程的堆栈没有变化)
- 尝试补救:
- 执行
thread @@kill name=?
尝试将执行中的任务中断; - 继续观察这个线程后续的堆栈是否有发生变化,若发生变化了,则视为解决hang问题;若没有发生变化,则表示此线程大概率陷入死循环,则只能暂时接受剩余正常线程继续工作。
- 执行
- 场景2:当发现池中所有线程都hang了(多个时间点的所有堆栈不再变化)且completed_task不再继续增长
- 尝试补救:
- 执行
thread @@kill poolname=?
将整个线程池内部进行shutdown操作(一些重要的心跳检测、主从延迟检测等任务也会停掉,在恢复之前可以通过查看心跳信息发现所有的实例中的STOP均为true,所有实例均不可用); - 若pool_size为0,表示连接池shutdown成功;继续执行
thread @@recover poolname=?
命令,以及观察pool_size恢复原来的初始值,completed_task、total_task均在重新增长(重要的心跳检测、主从延迟检测等任务也会恢复)。 - 若pool_size为2保持不变,则表示此线程池可能陷入死循环,只能通过重启解决此现象。
- 执行
- 注意:
thread @@kill name='0-TimerScheduler'
+thread @@kill name='1-TimerScheduler'
不等于thread @@kill poolname='TimerScheduler'
- Timer
- 与TimerScheduler类似处理
- frontWorker
- 场景1:当发现存在单个线程hang了(多个时间点这个线程的堆栈没有变化)
- 尝试补救:
- 执行
thread @@kill name=?
尝试将执行中的任务中断; - 若active_count值不变,后续单独观察此线程后续堆栈是否有变化,若堆栈有变化表示已解决此线程hang问题,若堆栈无变化,则表示此线程可能陷入死循环了,在影响面较大的情况下只能通过重启解决,影响面较小的情况,可暂时接受。
- 若active_count值减少,则继续执行
thread @@recover name=?
, 观察active_count值+1 (注意,这里如果name为'0-frontWorker',实际上恢复的可能并不是'0-frontWorker',具体原因与实现机制有关,这里不做阐述)。
- 执行
- 强烈建议:不要将所有的常驻线程,都被kill,否则会引起dble不可正常使用。
- managerFrontWorker
- 与frontWorker类似
- writeToBackendWorker
- 与frontWorker类似
- NIOFrontRW,进行前端网络IO吞吐的线程数
- 不支持kill/recover命令(因为kill掉NIOFrontRW会影响已经建立的前侧连接的业务执行)
- NIOBackendRW,进行后端网络IO吞吐的线程数
- 1.与frontWorker类似
- 2.在做recover之后,dble中的旧连接池中的连接不再由此RW维护了;因此需要做些对后端连接的刷新操作,比如:fresh conn forced where dbGroup ='groupName'用来刷新连接池 或者执行
reload @@config_all -r
重置连接池。 - 注意,当kill掉这个线程时,dble中内部与后端连接会收到影响:比如心跳不正常、执行sql有时hang有时can't reach等 凡事与后端实例交互的都会收到牵连,因此,不要随意使用kill命令。
- backendWorker
- 场景1:当发现存在单个线程hang了(多个时间点这个线程的堆栈没有变化),
- 尝试补救:
- 执行
thread @@kill name=?
尝试将执行中的任务中断; - 继续单独观察此线程后续堆栈是否有变化,若堆栈有变化表示已解决此线程hang问题,若堆栈无变化,则表示此线程可能陷入死循环了,在影响面较大的情况下只能通过重启解决,影响面较小的情况,可暂时接受。
- 此线程无需执行
thread @@recover
命令。
- 执行
- complexQueryWorker
- 与backendWorker类似
- 其他线程,可通过jstack等工具观测
- 与backendWorker类似,但建议不要轻易
thread @@kill name=?
命令
- 与backendWorker类似,但建议不要轻易
以上线程检测、print、kill、recover操作均会记录到logs/therad.log中,可自行演练观察日志。