文章主要讲解DPC的关键线程
DPC的线程池分为底层公共线程池和上层专用线程池,上层专用线程池使用底层公共线程池资源
底层公共线程池是DM系统中所有线程的基础资源池。它提供了一组通用的线程资源,供上层的各种专用线程池使用。
初始化数量和上限由THRDS_POOL_INIT,THRDS_POOL_MAX控制
THRDS_POOL_INIT:表示底层公共线程池初始化线程数
THRDS_POOL_MAX:底层公共线程池上限
使用临时线程的代价通常高于使用初始化好的线程池,因此在确定业务模型时需要确定合理的初始化线程数目。
基于底层公共线程池,DM提供了多个专用线程池,分别用于处理不同类型的任务。
目前已有专用线程池:SESS,ESESS,PTHD,NTSK,四个线程池均从底层公共线程池申请线程
SESS:会话线程池,单机上每个会话都是一个会话线程
ESESS:子会话会话线程池、DPC分为主会话和子会话
PTHD:并发线程池,执行并行任务
NTSK:任务线程池,通用任务处理
(线程池模块除了ESESS是DPC下独有的,另外三个是通用的)
体系图
达梦DPC采用两种线程管理模式应对不同场景:
三层结构:同步层→队列层→异步层
适用场景:存在线程间数据交换场景
瓶颈:工作队列访问制约大数据传输
生产者线程负责生成任务并将任务放入工作队列中;消费者线程从工作队列中取出任务进行处理。这种模式的核心是通过一个共享的工作队列来解耦生产者和消费者,使得它们可以独立运行。
例如
假设有一个系统,生产者线程负责从系统中读取数据并将其存入工作队列,消费者线程从工作队列中取出数据并进行数据分析。这种模式下,数据的读取和处理是解耦的,生产者和消费者可以并行工作。但如果数量非常大,工作队列的访问操作可能会成为瓶颈,导致系统性能下降。
领导者跟随者模式是一种高效的线程调度模式,主要目的是减少线程间的通信开销,提高在大数据量场景下的性能。
线程池中的线程可以处于三种状态:领导者、追随者和工作者。
无数据交换:避免线程通信开销
优势:大数据量场景性能卓越
1.THREADS 记录当前系统中活动线程的信息 --(重点)
2.DPC_STASK_THRD 看每个子任务、每个线程的执行统计信息,记录的是原始信息 --(重点)
3.DPC_QC_HISTORY 记录查询调度总单元 QC 的历史信息,执行号与会话映射,通过会话号找未执行完成的执行号 --(重点)
上面三个视图在平时使用最多,它们之间可以互相关联
4.THRDS_POOL 底层线程池信息。
5.LATCHES 记录当前正在等待的线程信息
6.THRD_POOL_GRP 上层特定模块中线程池组的信息
显示系统中所有活动线程的信息。
列 数据类型 说明
1 ID BIGINT 线程 ID
2 NAME VARCHAR(128) 线程名
3 START_TIME DATETIME(6) 线程开始时间
4 THREAD_DESC VARCHAR(1024) 线程描述
5 SESS_ID BIGINT 与本线程关联的会话 ID。NULL 表示无关联的会话
6 PROCESSOR_ID INTEGER 线程绑定的 CPU 处理器 ID。65535 表示未绑定 CPU 处理器
7 WAIT_SEQ INTEGER 线程等待序号
8 WAIT_STATUS VARCHAR(128) 线程等待状态
9 WAIT_TIME INTEGER 线程等待时
重点关注
ID 线程id, SESS_ID
看每个子任务、每个线程的执行统计信息。当 DM.INI 参数 ENABLE_MONITOR=1 时有效。
列 数据类型 说明
1 EXEC_ID BIGINT 执行号
2 MPP_EXEC_ID BIGINT 内部的表示执行号
3 STASK_NO INTEGER 子任务序号
4 THRD_NO INTEGER 工作线程序号
5 PLAN_NO INTEGER 子计划序号
6 START_TIME DATETIME(0) 当前线程的开始工作时间
7 TIME_USED BIGINT 当前线程执行子任务的耗时,单位毫秒
8 FIRST_ROW_USED BIGINT 返回第一行数据的耗时,单位毫秒
9 N_ROWS_SEND BIGINT 子任务输出的行数,对于 HTAB 操作符为存储的行数
10 N_BYTES_SEND BIGINT 子任务输出的数据大小,字节为单位
11 N_ROWS_RECV VARCHAR(1024) 如果子任务有 ERECV 操作符,展示接收的行数
12 P_WAIT_TIMES VARCHAR(1024) 打开限流时,请求资源操作的等待次数。未打开限流时,此值无意义
13 IS_OVER CHAR(1) 工作线程执行是否结束。Y 是;N 否
14 THRD_ID BIGINT 工作线程 ID
15 SESS_ID BIGINT 会话号
重点关注
EXEC_ID 执行号,STASK_NO 子任务序号,THRD_NO 工作线程序号,THRD_ID 工作线程id , SESS_ID 会话号
理解各个系统视图的列代表什么意思,才能更好的管理和监控线程
EXEC_ID: 执行号,每个sql执行时都会对应一个唯一的编号,sql和执行号 1:1
STASK_NO: 子任务序号,一个sql可能会有多个子任务,sql和子任务 1:多
THRD_ID: 工作线程id,一个子任务中可能会有多个线程参与,sql:子任务:工作线程关系为 1:多:多
THRD_NO: 工作线程序号,同一个节点上多个线程时,对每个线程从0开始进行编号(0、1、2、3...)
SESS_ID: 会话号,每个会话可能会有多个执行号(执行多个sql时)
EID: eid为全局id,一个eid对应多个sess_id,sql执行时,在DPC环境下会有多个会话id,主会话和子会话等
步骤1:定位问题会话
获取要监控语句的SESS_ID
select * from v$sessions;
步骤2:获取执行号
通过SEEE_ID来获取该语句的EXEC_ID,(未执行完成也可以找出执行号)
SELECT * FROM V$DPC_QC_HISTORY WHERE SESS_ID= 3178914464 AND IS_OVER='N';
步骤3:通过执行号来找线程信息
select *from V$DPC_STASK_THRD where exec_id='执行号'
通过DPC_STASK_THRD找出执行号的每个子任务和线程信息,可以找出关键线程并打印堆栈,方便后续进行问题定位
1.执行sql
select * from "分区表";
执行计划为:
1 #NSET2: [178975, 234684124, 2399]
2 #ERECV: [178975, 234684124, 2399]; stask_no(-1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
3 #ESEND: [178975, 234684124, 2399]; stask_no(0), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4 #GI: [178975, 234684124, 2399]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL)
5 #PRJT2: [178975, 234684124, 2399]; exp_num(57), is_atom(FALSE)
6 #CSCN2: [178975, 234684124, 2399]; INDEX44363485(分区表); btr_scan(1)
分析:
1.sql有一个任务号为-1,一个子任务号为0(stask_no(-1),stask_no(0))
2.子任务号stask_no(0),在每个节点上都有2个并行线程,一共10个节点,共20个线程(sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2))
2.找到执行sql的sess_id
select * from v$sessions where sql_text like '%分区表%'
sess_id=21374389816
3.通过sess_id找出sql的执行号
SELECT * FROM V$DPC_QC_HISTORY WHERE SESS_ID=21374389816 AND IS_OVER='N';
exec_id=25602591
4.通过执行号,去查线程号和每个子任务的发送行数,发送数据大小等
select *from V$DPC_STASK_THRD where exec_id='25602591'
EXEC_ID STASK_NO THRD_NO START_TIME TIME_USED FIRST_ROW_USED N_ROWS_SEND N_BYTES_SEND N_ROWS_RECV P_WAIT_TIMES IS_OVER THRD_ID SESS_ID
25602591 0 0 '2025-06-29 15:22:41' 50 1 12000 9185438 '0' '1' 'N' 26278 40576633656
25602591 0 1 '2025-06-29 15:22:41' 48 1 12000 8805399 '0' '1' 'N' 26546 40576633656
25602591 0 0 '2025-06-29 15:22:41' 43 1 12000 8798901 '0' '1' 'N' 1070482 2073750024
25602591 0 1 '2025-06-29 15:22:41' 44 1 12000 9190028 '0' '1' 'N' 1070939 2073750024
25602591 0 0 '2025-06-29 15:22:41' 68 1 12000 8818091 '0' '1' 'N' 1023129 21099444488
25602591 0 1 '2025-06-29 15:22:41' 47 1 12000 9191248 '0' '1' 'N' 1023698 21099444488
25602591 0 0 '2025-06-29 15:22:41' 49 1 12000 9191316 '0' '1' 'N' 3198344 1567324888
25602591 0 1 '2025-06-29 15:22:41' 51 1 12000 8815003 '0' '1' 'N' 3198946 1567324888
25602591 0 0 '2025-06-29 15:22:41' 74 1 12000 9188445 '0' '1' 'N' 1983565 30824611336
25602591 0 1 '2025-06-29 15:22:41' 52 1 12000 8812203 '0' '1' 'N' 1983763 30824611336
25602591 0 0 '2025-06-29 15:22:41' 45 1 12000 8814084 '0' '1' 'N' 2822995 1262619544
25602591 0 1 '2025-06-29 15:22:41' 42 1 12000 9180957 '0' '1' 'N' 2823571 1262619544
25602591 0 0 '2025-06-29 15:22:41' 48 1 12000 8806278 '0' '1' 'N' 1276154 782000872
25602591 0 1 '2025-06-29 15:22:41' 78 1 12000 9181329 '0' '1' 'N' 1276479 782000872
25602591 0 0 '2025-06-29 15:22:41' 56 1 12000 9188354 '0' '1' 'N' 469276 25510791064
25602591 0 1 '2025-06-29 15:22:41' 46 1 12000 8799863 '0' '1' 'N' 469460 25510791064
25602591 0 0 '2025-06-29 15:22:41' 46 1 12000 8800039 '0' '1' 'N' 2525446 5242871912
25602591 0 1 '2025-06-29 15:22:41' 52 1 12000 9190289 '0' '1' 'N' 2525863 5242871912
25602591 0 0 '2025-06-29 15:22:41' 44 1 12000 9177416 '0' '1' 'N' 860752 15651780616
25602591 0 1 '2025-06-29 15:22:41' 58 1 12000 8810859 '0' '1' 'N' 861636 15651780616
这个结果是最原始的信息,也可以通过写sql,去展示更清晰的线程收发数据情况
5.写sql进行详细展示
with v_empp_stat as
(select y.name name,
exec_id,
mpp_exec_id,
stask_no stask,
(thrd_no || ':T' || thrd_id) as thrd,
to_char(start_time,'HH24:MI:SS') as START,
time_used, --单位:毫秒
first_row_used, --单位:毫秒
n_rows_send as sent_row,
case when n_bytes_send >= 1024 * 1024 * 1024 then cast(n_bytes_send / 1024.0 / 1024 / 1024 as decimal(38,3)) || 'G' when n_bytes_send >= 1024 * 1024 then cast(n_bytes_send / 1024.0 / 1024 as decimal(38,3)) || 'M' when n_bytes_send = 0 then '0' else cast(n_bytes_send / 1024.0 as decimal(38,3)) || 'K' end as SENT,
n_rows_recv as recv_row,
P_WAIT_TIMES wait_times,
is_over
from SYS.V$DPC_STASK_THRD x,
SYS.DPC_BP_RAFT y
where SF_GET_EP_SEQNO(x.rowid) = y.raft_id and plan_no = 0
order by stask_no,
name,
thrd_no
)
,
v_empp_stat_ as
(select * from v_empp_stat where exec_id = '25602591' --执行号
)
select name, --exec_id, mpp_exec_id,
stask,
thrd,
is_over "OVER",
start,
time_used,
first_row_used,
sent_row ,
sent,
case when (time_used - first_row_used) > 0 then cast(sent_row * 1000.0 / (time_used - first_row_used) as int) when first_row_used > 0 then cast(sent_row * 1000.0 / first_row_used as int) else 0 end as RPS, --每秒发送行数rows per second
wait_times,
recv_row
from v_empp_stat_;
NAME STASK THRD OVER START TIME_USED FIRST_ROW_USED SENT_ROW SENT RPS WAIT_TIMES RECV_ROW
'RAFT_1' 0 '0:T3198344' 'N' '15:22:41' 49 1 12000 '8.766M' 250000 '1' '0'
'RAFT_1' 0 '1:T3198946' 'N' '15:22:41' 51 1 12000 '8.407M' 240000 '1' '0'
'RAFT_10' 0 '0:T1983565' 'N' '15:22:41' 74 1 12000 '8.763M' 164384 '1' '0'
'RAFT_10' 0 '1:T1983763' 'N' '15:22:41' 52 1 12000 '8.404M' 235294 '1' '0'
'RAFT_2' 0 '0:T2525446' 'N' '15:22:41' 46 1 12000 '8.392M' 266667 '1' '0'
'RAFT_2' 0 '1:T2525863' 'N' '15:22:41' 52 1 12000 '8.765M' 235294 '1' '0'
'RAFT_3' 0 '0:T1276154' 'N' '15:22:41' 48 1 12000 '8.398M' 255319 '1' '0'
'RAFT_3' 0 '1:T1276479' 'N' '15:22:41' 78 1 12000 '8.756M' 155844 '1' '0'
'RAFT_4' 0 '0:T469276' 'N' '15:22:41' 56 1 12000 '8.763M' 218182 '1' '0'
'RAFT_4' 0 '1:T469460' 'N' '15:22:41' 46 1 12000 '8.392M' 266667 '1' '0'
'RAFT_5' 0 '0:T1023129' 'N' '15:22:41' 68 1 12000 '8.41M' 179104 '1' '0'
'RAFT_5' 0 '1:T1023698' 'N' '15:22:41' 47 1 12000 '8.765M' 260870 '1' '0'
'RAFT_6' 0 '0:T26278' 'N' '15:22:41' 50 1 12000 '8.76M' 244898 '1' '0'
'RAFT_6' 0 '1:T26546' 'N' '15:22:41' 48 1 12000 '8.397M' 255319 '1' '0'
'RAFT_7' 0 '0:T1070482' 'N' '15:22:41' 43 1 12000 '8.391M' 285714 '1' '0'
'RAFT_7' 0 '1:T1070939' 'N' '15:22:41' 44 1 12000 '8.764M' 279070 '1' '0'
'RAFT_8' 0 '0:T2822995' 'N' '15:22:41' 45 1 12000 '8.406M' 272727 '1' '0'
'RAFT_8' 0 '1:T2823571' 'N' '15:22:41' 42 1 12000 '8.756M' 292683 '1' '0'
'RAFT_9' 0 '0:T860752' 'N' '15:22:41' 44 1 12000 '8.752M' 279070 '1' '0'
'RAFT_9' 0 '1:T861636' 'N' '15:22:41' 58 1 12000 '8.403M' 210526 '1' '0'
可以看到在每个RAFT节点上,都有两个线程在工作,每个节点的线程按0、1编号
可以通过time_uesd来分析哪个节点上耗时更长,找出问题节点的对应线程,或者通过发送行和大小来分析数据是否均衡分布
更多其他数据库相关专栏:
1.数据库优化
数据库优化基本思路、索引详解、执行计划、统计信息、CBO原理、单表优化、多表优化、分布式优化、子查询、优化案例等
数据库优化(sql优化)专栏连接
2.达梦分布式数据库:
部署详细步骤(DEM)、备份还原实战、核心特性理解、使用心得、表分区方式详细介绍、表分区最佳实践、DPC架构详解等
达梦分布式DPC专栏连接
3.应用开发类
jdbc、hibernate、ibatis、mybatis、MyBatis-Plus、Spring、中间件mycat、Sharding-JDBC等
达梦数据库应用开发专栏连接