达梦分布式集群DPC_DPC线程深度解析_yxy

达梦分布式集群DPC_DPC线程深度解析

  • 1. DPC专用线程体系
    • 1.1 DPC线程池分类
      • 1.1.1 底层公共线程池
      • 1.1.2 上层专用线程池
    • 1.2 线程管理模式
      • 1.2.1 生产者-消费者模式
      • 1.2.2 领导者跟随者模式
  • 2. DPC线程相关视图
    • 2.1 THREADS
    • 2.2 DPC_STASK_THRD
    • 2.3 关键列解释
  • 3. DPC线程管理监控
    • 3.1 sql卡顿,找出关键线程分析
    • 3.2 完整sql执行示例

1. DPC专用线程体系

文章主要讲解DPC的关键线程

1.1 DPC线程池分类

DPC的线程池分为底层公共线程池和上层专用线程池,上层专用线程池使用底层公共线程池资源

1.1.1 底层公共线程池

底层公共线程池是DM系统中所有线程的基础资源池。它提供了一组通用的线程资源,供上层的各种专用线程池使用。

初始化数量和上限由THRDS_POOL_INIT,THRDS_POOL_MAX控制

THRDS_POOL_INIT:表示底层公共线程池初始化线程数
THRDS_POOL_MAX:底层公共线程池上限

使用临时线程的代价通常高于使用初始化好的线程池,因此在确定业务模型时需要确定合理的初始化线程数目。

1.1.2 上层专用线程池

基于底层公共线程池,DM提供了多个专用线程池,分别用于处理不同类型的任务。

目前已有专用线程池:SESS,ESESS,PTHD,NTSK,四个线程池均从底层公共线程池申请线程

SESS:会话线程池,单机上每个会话都是一个会话线程
ESESS:子会话会话线程池、DPC分为主会话和子会话
PTHD:并发线程池,执行并行任务
NTSK:任务线程池,通用任务处理
(线程池模块除了ESESS是DPC下独有的,另外三个是通用的)

体系图

底层公共线程池
SESS会话线程池
ESESS子会话线程池
PTHD并行线程池
NTSK任务线程池
领导者-追随者模式
生产者-消费者模式

1.2 线程管理模式

达梦DPC采用两种线程管理模式应对不同场景:

1.2.1 生产者-消费者模式

三层结构:同步层→队列层→异步层
适用场景:存在线程间数据交换场景
瓶颈:工作队列访问制约大数据传输

生产者线程负责生成任务并将任务放入工作队列中;消费者线程从工作队列中取出任务进行处理。这种模式的核心是通过一个共享的工作队列来解耦生产者和消费者,使得它们可以独立运行。

例如
假设有一个系统,生产者线程负责从系统中读取数据并将其存入工作队列,消费者线程从工作队列中取出数据并进行数据分析。这种模式下,数据的读取和处理是解耦的,生产者和消费者可以并行工作。但如果数量非常大,工作队列的访问操作可能会成为瓶颈,导致系统性能下降。

1.2.2 领导者跟随者模式

领导者跟随者模式是一种高效的线程调度模式,主要目的是减少线程间的通信开销,提高在大数据量场景下的性能。
线程池中的线程可以处于三种状态:领导者、追随者和工作者
无数据交换:避免线程通信开销
优势:大数据量场景性能卓越

2. DPC线程相关视图

1.THREADS 记录当前系统中活动线程的信息 --(重点)
2.DPC_STASK_THRD 看每个子任务、每个线程的执行统计信息,记录的是原始信息 --(重点)
3.DPC_QC_HISTORY 记录查询调度总单元 QC 的历史信息,执行号与会话映射,通过会话号找未执行完成的执行号 --(重点)

上面三个视图在平时使用最多,它们之间可以互相关联

4.THRDS_POOL 底层线程池信息。
5.LATCHES 记录当前正在等待的线程信息
6.THRD_POOL_GRP 上层特定模块中线程池组的信息

2.1 THREADS

显示系统中所有活动线程的信息。

  列            数据类型       说明 
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

2.2 DPC_STASK_THRD

看每个子任务、每个线程的执行统计信息。当 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 会话号

2.3 关键列解释

理解各个系统视图的列代表什么意思,才能更好的管理和监控线程

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,主会话和子会话等

3. DPC线程管理监控

3.1 sql卡顿,找出关键线程分析

步骤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找出执行号的每个子任务和线程信息,可以找出关键线程并打印堆栈,方便后续进行问题定位

3.2 完整sql执行示例

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等
达梦数据库应用开发专栏连接

你可能感兴趣的:(达梦分布式集群,分布式,线程,DPC)