• [数据加载及处理] 如何处理数据集加载多进程(multiprocessing)错误
    在MindSpore的数据集加载接口中,可以启动多进程模式加快数据处理速度,具体有2个API支持:mindspore.dataset.GeneratorDatasetmindspore.dataset.GeneratorDataset(source, column_names=None, column_types=None, schema=None, num_samples=None, num_parallel_workers=1, shuffle=None, sampler=None, num_shards=None, shard_id=None, python_multiprocessing=True, max_rowsize=6)mindspore.dataset.mapmap(operations, input_columns=None, output_columns=None, column_order=None, num_parallel_workers=None, python_multiprocessing=False, cache=None, callbacks=None, max_rowsize=16, offload=None)这两个API中,与多进程启动相关的参数是python_multiprocessing和max_rowsize。python_multiprocessing决定是否启用多进程模式加快数据处理速度,而max_rowsize用于配置多进程模式下共享内存的大小,属于高级用法。一般来说,只需要将python_multiprocessing设置为True在大多数情况下就可以适用。△ 如果打开了多进程模式,可能会碰到以下的错误,这里总结了一些常见的错误和解决办法。错误1:核心报错信息是 This might be caused by insufficient shm, and the recommended shm size is at least 5 GB.原因:系统可用的共享内存太小,可以通过 df -h 查看共享内存的大小,如下示例有500G的共享内存,妥妥的够用啦。解决办法:如果发现自身系统的 /dev/shm 不足5G,那确实会导致这个错误,有2个解决办法关闭多进程模式,即设置python_multiprocessing=False。这样就会采用多线程模式进行加速,同样也会有一定的加速效果,也可以调节num_parallel_workers增加线程/进程数,以提高整体的数据处理效率。申请更多的共享内存,可以参考博客上教程调整共享内存,如 https://blog.csdn.net/Sunny_Future/article/details/100569637错误2:核心报错信息是 OSError: [Errno 24] Too many open files.这个错误可能会在 非root用户 下使用 多进程模式 时触发原因:有几个可能的原因如上一个错误所属,共享内存不足5G,导致无法使用。如果发现/dev/shm超过5G,却还是这个错误,则可能是 非root用户可用资源的限制,可以通过 ulimit -a 查看非root用户可用资源可以看到当前用户的可用 open files 数目只有2,这样很大程度就会抛出上述错误。解决办法:通过 ulimit -a 查看 open files 的数量,然后通过 ulimit -n 设置成较大的数值,如重新启动训练就可以啦。错误3:核心报错信息是 Insufficient shared memory available. Required: xxxxx, Available: xxxxx.这个错误可能会在 共享内存不够/处理的数据块过大 时触发原因:可能有其他进程也在使用共享内存,使用 df -h 查看 /dev/shm 当前的可用大小当前正在处理的数据非常大,导致其占用了过多的共享内存,使得共享内存不够用了解决办法:在允许的范围内,尽可能增大共享内存的大小,如 https://blog.csdn.net/Sunny_Future/article/details/100569637检查数据处理时,是否正在处理过大的数据导致内存占用太多通过 mindspore.dataset.config.set_prefetch_size() 减少数据管道中缓存的数据量实在不行,通过 mindspore.dataset.config.set_enable_shared_mem(False) 关掉共享内存
  • [POC&交付] 玩转GaussDB(DWS)线程管理问题系列(一) --- GaussDB for DWS线程残留定位手段
    【背景知识】DWS产品,基于公有云基础架构和平台的在线数据处理数据库,为用户提供海量数据挖掘和分析服务。主要提供不同行业的在线分析场景。如:增强ETL+实时BI分析、电商、IoT场景。当前的分布式架构,简单来说,是以多个组件分工负责不同的任务,构成整个集群,以下简单介绍部分组件,后续对各个组件详细介绍:CN:Coordinatornode,协调节点。客户端连接CN,发送SQL,CN接收解析后发送给所有DN执行,执行完后返回结果给CN,由CN统一返回给客户端。DN:Data node,数据存储节点,参与“本地数据”运算。GTM:全局事务管理器,系统中只有一个,采用主备方式,多线程架构。主要用来管理Id和快照信息,保证系统中全局事务的一致性。由于DWS是分布式架构,所以会涉及到整个集群的各个节点之间的互相通信,建立连接等通信相关功能,对于通信我们也有完善的一套架构方案,此处要知道的是我们通过cn与dn之间建立连接,互相通信,完成整个作业的执行,执行完作业之后,该连接会被回收。         本文就是介绍曾经某个据点出现过的一个cn进行了进程kill,此时cn进程的连接清理之后,dn有连接的时候,可以使用的分析定位手段。【视图介绍】pg_stat_activity:查询作业执行情况。pg_thread_wait_status:查询节点各个线程执行等待的情况。【定位手段】针对dn是否有线程残留,可以采用以下方法进行定位。1、确定是否是dn线程残留,如dn_6015_6016,表为postgres,cn查以下视图:select count(*) from pgxc_thread_wait_status where node_name='dn_6015_6016' and db_name='postgres' and thread_name='cn_5001' and  tlevel=0;解释:tlevel = 0代表stream处理层的上层,表示cn和dn之间的连接。select count(*) from pg_pooler_status where node_name='dn_6015_6016' and database='postgres'解释:查看此时dn_6015_6016的对应连接数量。此视图只能在cn查询,显示本地cn的pooler模块连接缓存信息。二者结果不同:第二个语句查询结果大于第一个语句。可以说明有连接残留。2、若发生线程残留,从pgxc_thread_wait_status 视图中找到dn上残留的线程号(即比pooler视图中多的线程号),ssh 到dn_6015_6016所在机器,登录到dn_6015_6016select * from pg_stat_activity where pid=?;解释:用该视图可以查询到client_port,client_port是与当前实例用于TCP连接的后台客户端的端口号。找到端口号例如:50079 、44101。3、在当前线程残留dn查看该端口号状态,使用netstat命令,查看对端ip。如下:Netstat –anop | grep 50079可以得到类似如下结果:解释:一个连接由一个五元组唯一确定:协议名(tcp),本端ip,本端端口号,对端ip,对端端口号,决定,与图中对应。(a:所有有效连接 n:使用ip地址代替名称 p:显示建立相关连接的程序名和PID  o:显示与网络计时器相关信息)当前图片中可以看到,对端是192.100.12.6:50079,此时可以ssh到对端,查看是否是cn,并且用同样命令:netstat -anop|grep 50079查看cn节点的进程号是否与上述命令结果中的连接中的相同:连接进程号即结果中xxxxx/gaussdb的xxxxx。如上图中的1240/gaussdb,1240即进程号。cn进程号可以通过:ps aux | grep coo查看。如下:此时可以判断该连接是由cn建立,与dn连接。此时确定连接还是有在使用的。【更换思路】一、这个时候线索断了,换一种思路,以dn上作业开始执行的时间对应在cn查询:cn_5003上执行:select * from pg_stat_activity order by query_start;这里发现有语句可以用开始的时间可以和dn上的作业开始时间对应上,并且语句完全一样,只是此时的queryid已经被置为0(因此一开始在cn没有查到),并且状态是idel in transaction,这个状态代表事务未提交。二、此时查看对应线程的cn和dn之间的连接视图(前面有结果):可以看到对应线程号还是 inuse状态,表明连接不是残留。三、根据cn上的连接client_port,继续看当前是谁连接了该cn,占用连接:这里发现是有java进程一直占用连接,说明客户的客户端一直在连接事务一直没有commit。【问题解决】是客户的误操作,不是连接残留,但是既然客户有疑问,那就要分析清楚。此篇博文可应用于所有连接残留问题定位分析。
  • [POC&交付] GaussDB for DWS Hang问题定位指南
    1 Hang问题基础知识GaussDB for DWS 为分布式数据库,通常由于单节点亚健康、系统资源紧张或查询本身的计划等问题,造成系统疑似发生Hang。Hang问题的产生原因由很多种,比如,死锁等待、日志同步等待、事务超时、通信故障、数据溢出发生死循环等等,更为常见的是由于执行慢、中间结果集倾斜而导致的疑似Hang。掌握Hang问题的基本定位方法对于大集群环境下快速找准疑似阻塞点,修复故障环境或优化执行性能是至关重要的。1.1 常用视图目前,GaussDB for DWS对外提供诸多系统视图,可以用来辅助Hang问题的分析定位,常用视图及用法说明如下表所示。(☆代表常用程度) pgxc_stat_activity ☆☆☆查询当前集群所有DN实例上各个session的信息,重点关注正在执行(state状态为active)的SQL。我们一般首先分析pgxc_stat_activity中的内容,初步根据执行时间筛选出疑似query,然后使用此query的query_id和视图pgxc_thread_wait_status结合,获取此query集群级别的线程状态进行hang问题分析。 注:此视图需要以超级用户的身份来运行 在问题分析时,我们首先需要客户反馈的疑似hang的作业的信息,根据作业所在的databse、运行作业的用户身份、运行作业客户端以及运行作业所连接的实例CN名称初步筛选出问题SQL的运行信息,具体运行信息见附件中字段含义解释。比如疑似hang作业是以omm用户运行在postgres数据库连接到cn_5001实例上运行的,我们使用如下SQL进行作业状态查询 我们可以根据上述返回的结果,结合作业运行的客户端名称、客户端ip、作业query运行开始时间query_start进一步筛选出疑似hang作业。 【附:视图各字段含义】字段名称数据类型字段描述coornametext运行业务SQL的CN节点名称datidoid用户会话在后台连接到的数据库OID。datnamename用户会话在后台连接到的数据库名称。可以看到此语句实际在哪个数据库运行,来帮助区分是什么业务。pidbigint后台线程ID。即运行此SQL的线程的线程号,此线程号在pg_thread_wait_status/pg_locks等视图中也存在,可以用这个线程号来与这些视图进行关联。usesysidoid登录该后台的用户OID。usenamename登录该后台的用户名。执行此SQL的用户名,可以用来确认是什么用户调起的业务。application_nametext连接到该后台的应用名。应用名称,一般有三种:l gsql:此SQL是从gsql客户端发起l Data Studio:从Data Studio客户端发起的SQLl cn_50XX:代表从远端CN发送过来的SQL语句,一般出现在DDL、DCL、analyze、vacuum作业场景下l 其它:其它业务客户端发起的业务连接,如果应用程序未显式命名application_name,数据侧一般显式为unkonwnclient_addrinet连接到该后台的客户端的IP地址。 如果此字段是null,它表明通过服务器机器上UNIX套接字连接客户端或者这是内部进程(如autovacuum)。可以用来确定是从哪个机器发起的连接,进而帮助确认是什么业务发起的语句client_hostnametext客户端的主机名,这个字段是通过client_addr的反向DNS查找得到。这个字段只有在启动log_hostname且使用IP连接时才非空。client_portinteger客户端用于与后台通讯的TCP端口号,如果使用Unix套接字,则为-1。通过此字段圈定业务SQL发起客户端backend_starttimestamp with time zone该后台线程启动的时间,即当客户端连接到服务器的时间。xact_starttimestamp with time zone启动当前事务的时间,如果没有事务是活跃的,则为null。如果当前查询是首个事务,则这列等同于query_start列。如果启用了显式事务,即使用begin/start transaction等开启了一个事务,则此字段代表事务开始的时间。query_starttimestamp with time zone开始当前活跃SQL的开始时间, 如果state的值不是active,则这个值是上一个SQL的开始执行时间state_changetimestamp with time zone上次state字段值变化的时间。waitingboolean如果后台当前正等待锁则为true,否则为falseenqueuetext工作负载管理资源状态。· 语句当前的排队情况,包括:· Global::在全局队列中排队。 · Respool:在资源池队列中排队。 · CentralQueue:在中心协调节点(CCN)中排队。 · Transaction:语句处于一个事务块中排队。 · StoredProc : 语句处于一个存储过程中排队。 · None:未在排队。 · Forced None : 事务块语句或存储过程语句由于超出设定的等待时间而强制执行。上面这些状态表明了当前语句的排队情况,如果处在排队状态,语句是不在执行的,也不会占用系统资源。statetext该后台当前总体状态。状态值可能是如下的一种:· active:后台正在执行一个作业。 · idle:后台线程空闲,且不在一个事务块中。 · idle in transaction:后台线程空闲,且在事务块中· idle in transaction (aborted):后台线程空闲,且后台线程在事务块中有语句执行失败且没有执行rollback命令 · fastpath function call:后台正在执行一个fast-path函数。 · disabled:如果后台禁用track_activities,则报告这个状态。resource_poolname用户作业关联的资源池。query_idbigint查询语句的ID。当前语句的唯一标识,可以用此字段与pg_thread_wait_status关联。进一步进行问题分析定位querytext该后台的最新查询。如果state状态是active(活跃的),此字段显示当前正在执行的查询。所有其他情况表示上一个查询。语句默认仅显示1024字节。 pgxc_thread_wait_status  ☆☆☆☆☆查询集群全局所有线程的层次调用关系及阻塞等待情况,通常在视图查询语句增加其他过滤条件(比如根据pgxc_stat_activity筛选出来的疑似hang的SQL的query_id),缩小关注排查范围。【附:视图各字段含义】字段名称数据类型字段含义描述node_nametext实例的名称。db_nametext数据库名称。thread_nametext线程名称。query_idbigint业务SQL的ID编号,同一条SQL对应的所有的执行线程的query_id是相同的,与pg_stat_activity中query_id一致tidbigint当前线程的线程号。与pgxc_stat_activity中的pid、pg_locks中的pid一致lwtidinteger当前线程的轻量级线程号。使用此线程号可以在所在节点上进行gstack获取此线程运行栈信息ptidintegerstreaming线程的父线程号tlevelintegerstreaming线程的层级。与执行计划的层级(id)相对应。smpidintegersmp执行模式下并行线程的并行编号。wait_statustext当前线程的等待状态。等待状态的详细信息请参见资料。一般常见的状态有:wait node:dn_xxxx_xxxx:表示在等待某个DNnone:表示正在执行,没有等待任何其他节点acquire lock :表示正在等待锁。这时需要到对应节点上查看pg_locks视图。 pg_thread_wait_status  ☆☆☆☆单个实例上所有作业线程的层次调用关系及阻塞等待情况,在大集群复杂query问题定位时,视图pgxc_thread_wait_status返回的信息过多,会对问题定位形成一定干扰,这时可以在CN上通过execute direct on语法获取指定dn实例的作业线程信息。比如要获取dn_6001_dn_6002节点的作业线程调用信息 pgxc_comm_recv_stream  ☆☆☆查询集群所有DN的通信库接收流状态,辅助通信层发生收发Hang的排查定位。pgxc_comm_send_stream  ☆☆☆查询集群所有DN的通信库发送流状态,与pgxc_comm_recv_stream视图结合使用,来定位通信层的收发Hang问题。pgxc_prepared_xacts  ☆☆查询集群中所有节点的启动事务信息,辅助事务超时场景下的Hang问题定位,通过查询该视图获取gxid,然后结合pgxc_xacts_iscommitted(gxid)可以获知事务是否提交。pgxc_running_xacts  ☆☆查询集群中所有节点的运行事务信息。pg_locks  ☆☆查询当前实例的锁状态,辅助死锁等待的Hang问题定位。名称类型引用描述locktypetext-被锁定对象的类型:relation,extend,page,tuple,transactionid,virtualxid,object,userlock,advisory。databaseoidPG_DATABASE.oid被锁定对象所在数据库的OID。· 如果被锁定的对象是共享对象,则OID为0。 · 如果是一个事务ID,则为NULL。relationoidPG_CLASS.oid表的OID,如果锁定的对象不是表,也不是表的一部分,则为NULL。锁等待一般都是等待表的锁,用relation做条件可以看到当前的表锁被谁持有。pageinteger-关系内部的页面编号,如果对象不是关系页或者不是行页,则为NULL。tuplesmallint-页面里边的行编号,如果对象不是行,则为NULL。virtualxidtext-事务的虚拟ID,如果对象不是一个虚拟事务ID,则为NULL。transactionidxid-事务的ID,如果对象不是一个事务ID,则为NULL。classidoidPG_CLASS.oid包含该对象的系统表的OID,如果对象不是普通的数据库对象,则为NULL。objidoid-对象在其系统表内的OID,如果对象不是普通数据库对象,则为NULL。objsubidsmallint-对于表的一个字段,这是字段编号;对于其他对象类型,这个字段是零;如果这个对象不是普通数据库对象,则为NULL。virtualtransactiontext-持有此锁或者在等待此锁的事务的虚拟ID。pidbigint-持有或者等待这个锁的服务器线程的逻辑ID。如果锁是被一个预备事务持有的,则为NULL。语句pid,可以看到这个pid是在等待哪几各锁,持有哪几各锁。modetext-这个线程持有的或者是期望的锁模式。grantedboolean-· 如果锁是持有锁,则为TRUE。 · 如果锁是等待锁,则为FALSE。表示锁被谁持有fastpathboolean-如果通过fast-path获得锁,则为TRUE;如果通过主要的锁表获得,则为FALSE。 pgxc_node  ☆☆查询集群中所有实例节点信息,重点关注节点的node_name, node_port, node_id。除过上述常用视图,Hang问题定位过程需要根据实际场景,结合执行计划、gstack工具、系统日志等共同分析定位。1.2 简单示例比如在执行create table的时候疑似发生hang,那么我们可以执行以下操作定位问题1. 获取疑似hang的SQL的query_idselect * from pgxc_stat_activity where state = ‘active’ and lower(query) like ‘create table %’;2. 获取此作业的线程等待关系select * from pgxc_thread_wait_status where query_id = xx;xx:为上一步获取的疑似hang的SQL的query_id值 分析此query_id相关的线程的状态(字段wait_status),查看是否有acquire lock状态的线程,找到其node_name 和 tid字段3. 到对应的node_name上获取锁信息execute direct on (xx) ‘select * from pg_locks where pid = xxx’;--xx: 上一步获取的node_name字段的值--yy:上一步获取的tid字段值。获取等待加锁的表信息(字段relation)4.到对应的node_name上获取此表的持锁信息execute direct on (xx) ‘select * from pg_locks where relation = yy and granted = true’;--xx: 第二步获取的node_name字段的值--yy: 第三步获取的等待加锁字段的值获取持锁的线程(字段pid)5.获取持锁的作业信息select * from pgxc_stat_activity where pid=xx;--xx: 第四步获取持锁线程信息字段query的内容即为持锁线程信息,也是阻塞create table语句的作业信息 2 Hang问题分类客户侧感知的hang分为三种1. 真实hang一般是轻量级锁缺陷、执行链路环状或者死循环执行。这种场景下作业永远无法执行完2. 执行慢:业务执行慢,远远超出客户的预期,客户侧产生hang的认知效果。这种问题最终需要通过调优解决3. 锁等待:因抢占不到锁资源,导致作业排队等待加锁。这种场景的表现是要么作业执行时间边长,要么等待一段时间(一般为20min)之后报锁超时的失败信息 根据以往的经验,局点常见的hang问题有以下几种3 Hang问题定位方法及解决措施3.1 基本步骤Step1. cm_ctl query查询集群当前状态,确保集群状态正常;Step2. gsql连接数据库,执行select * from pgxc_stat_activity,查询目标查询的query_id,有时候可以增加where state = ‘active’筛选活跃SQL;Step3. 执行select * from pgxc_thread_wait_status where query_id = xxx,查询集群全局与之关联的所有线程的层次调用关系及阻塞等待情况。自上而下,逐层分析,确定疑似阻塞节点及线程信息,甚至,可以绘制线程等待关系图,更加直观地分析当前Hang问题的根因。除此,可结合执行计划或gstack查看线程堆栈,进一步佐证定位结论。Step4. 如果Step3仍无定论,则针对其他常见Hang场景(目前以锁等待和通信收发等待最为常见),结合2.1节相应视图,进一步分析定位:a) 锁等待:从Step3的查询结果分析线程等待关系,如果阻塞线程状态为acquire lock,则进一步执行select * from pg_locks where pid = xxx查询阻塞线程的加锁情况;b) 通信层数据收发成环:从Step3的查询结果分析线程等待关系,如果阻塞线程状态一直为flush data: wait quota,则可能是通信层收发过程hang死,继续执行select * from pgxc_comm_send_stream和select * from pgxc_comm_recv_stream,可以根据Step3的查询结果增加where条件限定node_name、remote_name、query_id、pn_id(即plevel),进一步排查wait quota的根因是否是发送端或接收端数据流异常或者通信问题等。
  • [技术干货] rm -rf之后磁盘空间没有释放的解决方法
    现在运营的平台采用的是微服务架构,部署的服务较多,开发环境一台服务器上部署几十个服务是很正常的,之前用CI构建部署的时候,发现磁盘满了。我的处理方式是:先删了 /tmp/ 目录, 空闲出部分空间,然后检查下几个常用的用户目录,发现几个日志文件占用了好几个大G,由于是开发环境,所以就直接删除, 于是 rm之后就天真地认为万事大吉了...然而过几天后,又有同事发现该机器磁盘又满了,惊呼奇怪咋这么快又满了。最终发现是上次 rm后,占用好几个大G的日志文件被删除之后,磁盘空间并没有释放。Linux系统中是通过link的数量来控制文件删除的,只有当一个文件不存在任何link的时候,这个文件才会被删除。一般来说,每个文件都有2个link计数器:i_count 和 i_nlink,也就是说:Linux系统中只有i_nlink及i_count都为0的时候,这个文件才会真正被删除。 • i_count表示当前文件使用者(或被调用)的数量•  i_nlink表示介质连接的数量(硬链接的数量);可以理解为i_count是内存引用计数器,i_nlink是磁盘的引用计数器。 当一个文件被某一个进程引用时,对应i_count数就会增加;当创建文件的硬链接的时候,对应i_nlink数就会增加。在Linux或者Unix系统中,通过rm或者文件管理器删除文件,只是将它会从文件系统的目录结构上解除链接(unlink),实际上就是减少磁盘引用计数i_nlink,但是并不会减少i_count数。如果一个文件正在被某个进程调用,用户使用rm命令把文件"删除"了,这时候通过ls等文件管理命令就无法找到这个文件了,但是并不意味着这个文件真正的从磁盘上删除了。因为还有一个进程在正常的执行,在向文件中读取或写入,也就是说文件其实并没有被真正的"删除",所以磁盘空间也就会一直被占用。当服务进程停止(文件句柄的引用计数会变为0)或者重启后,占用的存储空间才被释放。如果不知道具体进程或文件名的话:lsof | grep deleted,这样会查找所有被删除的但是文件句柄没有释放的文件和相应的进程,然后再kill掉进程或者重启进程即可。其实可以简单用修改文件内容的方式(例如echo "test">test.log)在不用重启进程的情况下释放空间。du和ls的区别对于某个日志文件,直接用echo "test" > yyzx_tradingcenterservice.log.2020-06-03.log,然后 df 确认磁盘空间确实已经释放,分别执行下面的命令:1234[dev@tjptdebug-no yyzx_tradingcenterservice_logs]$ du -h  yyzx_tradingcenterservice.log.2020-06-03.log 4.0K    yyzx_tradingcenterservice.log.2020-06-03.log[dev@tjptdebug-no yyzx_tradingcenterservice_logs]$ ll -h  yyzx_tradingcenterservice.log.2020-06-03.log-rw-rw-r-- 1 dev dev 7 Jul  8 19:49 yyzx_tradingcenterservice.log.2020-06-03.logls 的结果是 apparent sizes,我的理解是文件长度,就类似文件系统中 file 这个数据结构中的定义文件长度的这个字段,du 的结果 disk usage,即真正占用存储空间的大小,且默认度量单位是 block。block 为磁盘存储的基本的单位,方便磁盘寻址等,而此处的block可以理解为一个逻辑单位,且一个文件除了包括数据外,还需要存储描述此文件的其他信息,因此包含1个字节的文件实际在磁盘中占用的存储空间不止1个字节。总结du == disk usage (磁盘使用量,占用的磁盘空间)ls == apparent sizes(文件长度,file数据结构中定义的文件长度字段)一个文件占用的磁盘空间和一个文件的大小是两码事情。占用空间取决于文件系统的块(block)的大小,Linux一般默认是4k(4096) ,因此,一个大小为1个字节的文件,最小也要占用4k,如果你创建文件系统的时候制定块大小是16K,那么即便一个文件只有1个字节,占用空间也是16K。通常情况下,ls 显示的文件大小比du显示的磁盘占用空间小,比如文件系统的block是4K,一个13K的文件占用的空间是 13k/4k = 3.25 个block,一个block只能被一个文件占用,因此实际占用空间就是4个block,就是16K。到此这篇关于rm -rf之后磁盘空间没有释放的解决方法的文章就介绍到这了
  • [存储] 打破黑盒子——GaussDB(DWS)导入导出业务监控方法总结
    GDS、COPY、\COPY 等工具是 GaussDB(DWS) 提供的数据导入导出工具,常应用于数据迁移、数据同步、数据备份/恢复等场景。GDS 等工具在数仓日常维护中使用频率较高,难免遇到性能、功能问题,此时如果能监控到 GDS 在干什么、业务进度如何,必然能为排故提供有效参考,提高排查效率。本文从系统视图、运行日志两个方面介绍导入导出的监控方法,并配合实例进行说明,最后简要介绍 PostgreSQL 14 的 COPY 监控视图进行简单对比。1. 视图目前主要有2类系统视图可以对 GDS 业务进行不同角度的实时监控:backend状态业务执行状态下面分别对这两类视图进行解释说明。1.1 backend 状态通过系统视图 pg_stat_activity、pgxc_thread_wait_status,可以获知导入/导出backend线程的执行状态,如:是否 hang、是否死锁等。pg_stat_activity 一般会过滤 state 使用,通过在查询结果中寻找相应的 query,对所在记录进行观察分析。select * from pg_stat_activity where state = 'active';查询结果类似下图,图中红框框住的是刚刚发起 GDS 导入业务。pg_stat_activity 视图提供信息有限,可与视图 pgxc_thread_wait_status 进行关联查询等待状况,确认是否存在hang以及hang的直接原因:select wait.*, stat.query, stat.state, stat.query_start from pgxc_thread_wait_status wait inner join pg_stat_activity stat on wait.tid = stat.pid;查询结果类似下图,图中红框框住的是刚刚发起GDS导入业务,wait_status 列的内容是“wait node(total 3): datanode3”,表示CN此刻等待的节点共有3个,其中一个是datanode3。这个结果是与测试环境1CN 3DN一致的。1.2 业务执行状态2021年4月发布的 GaussDB(DWS) 8.1.1 开始提供导入导出实时监控视图:pg_bulkload_statistics、pgxc_bulkload_statistics、pgxc_bulkload_progress,支持对GDS、COPY、\COPY的实时监控。与既有约定一致,pgxc_bulkload_statistics、pgxc_bulkload_progress 是在 CN 上查询,用于获取整个集群的导入导出业务的实时状况,pg_bulkload_statistics 可在各节点上执行,用于获取当前节点的导入导出业务的实时状况。注意,这类视图需要有系统管理员权限才可以访问。pg_bulkload_statistics、pgxc_bulkload_statistics 视图字段一样,详细说明见下表。字段名称字段类型字段说明node_nametext节点名称db_nametext数据库名称query_idbigint查询IDtidbigint当前线程的线程号lwtidinteger当前线程的轻量级线程号session_idbigintGDS的会话IDdirectiontext业务类型,取值包括:gds to file、gds from file、gds to pipe、gds from pipe、copy from、copy to。querytext查询语句addresstext当前导入/导出外表的locationquery_starttimestamp导入/导出开始时间total_bytesbigint待处理数据的总大小。仅GDS普通文件导入时,且该行记录来自CN节点才会显示,否则为空。phasetext当前业务阶段,包括: INITIALIZING(初始化)、TRANSFER_DATA(传输中)、RELEASE_RESOURCE(结束)。done_linesbigint已传输行数done_bytesbigint已传输字节数pgxc_bulkload_progress 视图只可用于GDS普通文件导入业务场景,本质上是基于 pgxc_bulkload_statistics 视图的聚合结果,其字段说明如下:字段名称字段类型字段说明session_idbigintGDS的会话IDquery_idbigint查询IDquerytext查询语句progresstext业务进度百分比这类视图可以直接使用,或按需使用字段过滤,或者关联其他表/视图进行查询。查询集群级导入导出的场景举例:-- 查询集群所有的导入导出业务实时状况 select * from pgxc_bulkload_statistics; -- 查询集群导入业务的实时进度 select * from pgxc_bulkload_progress; -- 另外,也可以基于这类视图进行适当的计算,如计算各 DN 传输速率: select node_name, query, round(done_bytes / 1024 / extract(epoch from(current_timestamp - query_start)), 2) || 'kB/S' as avg_speed from pgxc_bulkload_statistics; -- 计算总导入/导出速率: with trans_bytes as (select query_id, query, sum(done_bytes) as total_bytes from pgxc_bulkload_statistics group by (query_id,query)) select trans_bytes.*, stat.query_start, round(total_bytes / 1024 / extract(epoch from(current_timestamp - stat.query_start)), 2) || 'kB/S' as speed from trans_bytes, pg_stat_activity as stat where trans_bytes.query_id = stat.query_id;以上样例的执行效果如图:查询节点级导入导出业务的场景举例:-- 查询当前节点的导入导出业务的实时进度 select * from pg_bulkload_statistics; -- 与pg_thread_wait_status关联查询,查看是否有hang select stat.*, wait.wait_status, wait.wait_event from pg_bulkload_statistics stat join pg_thread_wait_status wait on stat.tid = wait.tid;以上样例的执行效果如图:2. 日志导入导出业务会在不同的执行阶段向运行日志中写入业务状态记录,所以我们也可以通过日志查看执行状况。但是由于获取日志的不便性,以及分析日志的滞后性,日志获取状况是最最最后的方法,我们推荐实时视图作为导入导出监控的首选方法。2.1 CN/DN 日志根据业务的不同阶段和类型,打印日志内容如下表所示:初始化阶段数据传输阶段结束阶段gds importsession XXXXX gds import is in the INITIALIZING state.sesssion: XXXXX gds import is transforming from INITIALIZING to TRANSFER_DATA state.Session XXXXX gds import has transformed from TRANSFER_DATA to RELEASE_RESOURCE state.gds exportsession: XXXXX gds export is in the INITIALIZING state.session: XXXXX gds export is transforming from INITIALIZING to TRANSFER_DATA state.Session XXXXX gds export has transformed TRANSFER_DATA to RELEASE_RESOURCE state.copy fromcopy from is in the INITIALIZING state.copy from is transforming from INITIALIZING to TRANSFER_DATA state.copy from has transformed from TRANSFER_DATA to RELEASE_RESOURCE state.copy tocopy to is in the INITIALIZING of state.copy to is transforming from INITIALIZING to TRANSFER_DATA state.copy to has transformed from TRANSFER_DATA to RELEASE_RESOURCE state.2.2 GDS 日志GDS 通过设置启动参数 --debug-level 可以控制打印日志级别。GDS 在关键环节上也有日志打印,如下列出的日志内容是比较重要的日志,默认日志级别可见:(1)CN/DN 与 GDS 建连时:关键内容: [HandleAccept] "ip:port" connected to GDS, fd is xxx, ptr is xxx 日志示例: 2021-07-30 16:24:08.274 72442 MT LOG: [HandleAccept] "127.0.0.1:58570" connected to GDS, fd is 4, ptr is 0xfc95d0(2)CN/DN 将要关闭连接时(只有导出业务才会有,一般是先打印DN相关的关闭信息,最后是 CN):关键内容: Session XXXX close connection in HandleRead. addr: ip:port, conn ptr is xxx 日志示例:2021-07-31 10:34:42.345 3478 WT LOG: Session 81627743246173930 close connection in HandleRead. addr: 127.0.0.1:46600, conn ptr is 0xfc7bc0(3)CN/DN关闭连接时:关键内容:xxx closed. Event: READING/WRITING 日志示例:2021-07-30 16:24:36.208 3478 WT LOG: Session: 81627743246142387 HandleError Node: datanode1 IP: 127.0.0.1:58580, fd is 17, connect ptr is 0xfc81b0 closed. Event: READING(4)主线程成功把工作分配给工作线程:关键内容:Session xxx is being transfered to worker. 日志示例:2021-07-30 16:30:06.148 72442 MT LOG: Session 81627743246142569 is being transfered to worker.3. 结合实例,学以致用3.1 例1:发现copy性能瓶颈在 COPY 导出的时候,执行视图发现同一时刻只有1个DN的传输量在增加,表明是CN是阻塞地只接收某一DN到结束,然后才开始接收下一个DN数据,存在性能问题,说明copy有较大优化空间,可以尝试使用并行读取提高性能。3.2 例2:区分是执行慢还是hang某测试集群执行 GDS 业务比平常慢很多,耗时很长仍没有结束,怀疑是 GDS hang 住。通过查询导入导出视图发现传输数据是在缓慢增加的,进一步查看发现此刻集群上有其他导入导出业务在运行,此时集群已经承压较大,业务缓慢是正常现象,由此自证清白,排除了 hang 问题。4. 知识扩展:PostgreSQL是怎么做的?PostgreSQL 14 之前并没有专门针对导入导出业务的监控工具,如果要进行监控,只能使用 pg_stat_activity、pg_locks 等通用视图。社区也注意到了这方面的缺陷,故前不久发布的PostgreSQL 14新增了对COPY命令的实时监控视图 pg_stat_progress_copy,可监控所有正在执行的COPY命令,具体的代码提交请参考 ReportProgressOfCopyCommands。每当 COPY 运行时,pg_stat_progress_copy 视图将为当前运行 COPY 命令的每个 backend 构造一行记录。下表对视图字段进行说明:字段名称字段类型字段说明pidintegerbackend进程号datidoidbackend连接的database的oiddatnamenamebackend连接的database的名字relidoid执行COPY命令的表的OID。如果从SELECT查询复制,则设置为0。commandtext正在运行的命令为:COPY FROM或COPY TO。typetext用于读/写数据的IO类型:FILE, PROGRAM, PIPE(用于COPY from STDIN和COPY to STDOUT),或CALLBACK(例如在逻辑复制的初始表同步过程中使用)。bytes_processedbigintCOPY命令已经处理的字节数。bytes_totalbigintCOPY FROM命令中源文件的大小,以字节为单位。如果不可用,则设置为0。tuples_processedbigintCOPY命令已经处理的元组数目。tuples_excludedbigint由于COPY命令的WHERE子句排除了它们而未处理的元组的数目。以下是使用 PostgreSQL 14 Beta2 版本执行 COPY 文件导入时,查询监控视图的结果:对比可知,GaussDB(DWS) 导入导出监控视图提供的信息更全面,包括具体文件名、query 语句、执行时间、执行阶段等关键信息,基于此可以进行较为复杂的聚合计算,提供不同的监控视角;PostgreSQL 14 相对比较简单,但其字段 tuples_excluded 可以额外刻画 WHERE 过滤掉的tuples。
  • [集群&DWS] GaussDB(DWS)中多路IO复用介绍
    多路IO复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个线程。多路IO复用共有三种实现模式:selectpollepoll1、select1.1 select进行IO复用原理当一个客户端连接上服务器时,服务器就将其连接的fd加入fd_set集合,等到这个连接准备好读或写的时候,就通知程序进行IO操作,与客户端进行数据通信。大部分 Unix/Linux 都支持 select 函数,该函数用于探测多个文件描述符的状态变化。1.2 select函数原型int select( int maxfdp, //Winsock中此参数无意义 fd_set* readfds, //进行可读检测的Socket fd_set* writefds, //进行可写检测的Socket fd_set* exceptfds, //进行异常检测的Socket const struct timeval* timeout //非阻塞模式中设置最大等待时间 )1.3 使用select的步骤创建所关注的事件的描述符集合(fd_set),对于一个描述符,可以关注其上面的读(read)、写(write)、异常(exception)事件,所以通常,要创建三个fd_set,一个用来收集关注读事件的描述符,一个用来收集关注写事件的描述符,另外一个用来收集关注异常事件的描述符集合。调用select()等待事件发生。这里需要注意的一点是,select的阻塞与是否设置非阻塞I/O是没有关系的。轮询所有fd_set中的每一个fd,检查是否有相应的事件发生,如果有,就进行处理。2、pollpoll本质上和select没有太大区别,都是先创建一个关注事件的描述符的集合,然后再去等待这些事件发生,然后再轮询描述符集合,检查有没有事件发生,如果有,就进行处理。2.1 Poll使用流程创建描述符集合,设置关注的事件调用poll(),等待事件发生。下面是poll的原型:      int poll(struct pollfd *fds, nfds_t nfds, int timeout);      类似select,poll也可以设置等待时间,效果与select一样。轮询描述符集合,检查事件,处理事件。2.2 与select区别select需要为读、写、异常事件分别创建一个描述符集合,最后轮询的时候,需要分别轮询这三个集合。而poll只需要一个集合,在每个描述符对应的结构上分别设置读、写、异常事件,最后轮询的时候,可以同时检查三种事件。它没有最大连接数的限制,原因是它是基于链表来存储的。2.3 poll的缺点大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。 poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。3、epollpoll和select,它们的最大的问题就在于效率。它们的处理方式都是创建一个事件列表,然后把这个列表发给内核,返回的时候,再去轮询检查这个列表,这样在描述符比较多的应用中,效率就显得比较低下了。epoll是一种比较好的做法,它把描述符列表交给内核,一旦有事件发生,内核把发生事件的描述符列表通知给进程,这样就避免了轮询整个描述符列表。epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。epoll与select和poll的调用接口上的不同:select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。3.1 epoll的使用步骤创建一个epoll描述符,调用epoll_create()来完成。epoll_create()有一个整型的参数size,用来告诉内核,要创建一个有size个描述符的事件列表(集合)。      int epoll_create(int size)给描述符设置所关注的事件,并把它添加到内核的事件列表中。这里需要调用epoll_ctl()来完成。       int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)等待内核通知事件发生,得到发生事件的描述符的结构列表。该过程由epoll_wait()完成。得到事件列表后,就可以进行事件处理了。      int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)3.2 epoll的LT和ET的区别水平触发和边缘触发的区别:只要句柄满足某种状态,水平触发就会发出通知;而只有当句柄状态改变时,边缘触发才会发出通知。LT:水平触发,效率会低于ET触发,尤其在大并发,大流量的情况下。但是LT对代码编写要求比较低,不容易出现问题。LT模式服务编写上的表现是:只要有数据没有被获取,内核就不断通知你,因此不用担心事件丢失的情况。ET:边缘触发,效率非常高,在并发,大流量的情况下,会比LT少很多epoll的系统调用,因此效率高。但是对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况。3.3 epoll的优点没有最大并发连接的限制,能打开FD的上限远大于1024(1G的内存上能监听约10万个端口);效率提升。不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远高于select和poll。内存拷贝。epoll通过内核和用户空间共享一块内存来实现消息传递的。利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap 减少复制开销。epoll保证了每个fd在整个过程中只会拷贝一次(select,poll每次调用都要把fd集合从用户态往内核态拷贝一次)。
  • [其他] log4j漏洞规避过程中,发现找不到log.manager进程
    【问题现象】现场按照log4j漏洞规避手册执行规避操作,规避组件logManagerRun.sh后,查询进程:ps -ef |grep Dprocess.name=mppdb.log.manager |grep -v grep,发现没有问题原因:查看/var/log/Bigdata/mpp/healthCheck/logManagerScript.log日志,发现该进程去年4月份进程已经停止问题解决:手动执行 sh /opt/huawei/Bigdata/FusionInsight_MPPDB_8.0.0/install/FusionInsight-MPPDB-8.0.0/sbin/logManagerRun.sh start 拉起进程
  • [集群&DWS] GaussDB(DWS)stream线程池设计(一)
    Stream算子作为SQL join操作时频繁发生的执行算子,负责CN节点GATHER数据,DN节点REDISTRIBUTE和BROACAST数据。大集群高并发场景下,Stream算子过多可能会导致通信的性能瓶颈,引起性能劣化(2000个stream同时启动,进程初始化耗时从ms级劣化到s级),因此需要尽可能减少Stream算子。但是在某些现场环境下,存在数据倾斜、join查询不包含必要分布键等客观情况,Stream算子无法有效减少,为多表join场景下的查询时延保障带来挑战。stream线程是临时线程,随query启动和退出,负责stream算子的执行,stream线程初始化和退出都会争抢锁等进程级资源,在stream线程个数无法进一步优化的场景下,需要设计有效方案以减少stream线程初始化和退出的时间代价,将进程初始化耗时稳定在ms级,保障数据库的确定性时延查询。Stream线程未池化时的执行过程可以描述为:线程初始化—>线程任务执行—>线程退出。Stream线程池的核心思想是等stream线程执行完计划任务,保留必要且可复用的线程信息,将线程放入线程池中。线程池化后线程执行过程如下图一所示,可以描述为:步骤一线程信息初始化—>步骤二线程待唤醒后轻量级初始化(query级初始化)—>步骤三线程任务执行—>步骤四线程清理,返回步骤二。当线程等待超时、超出线程池容量(最大stream线程个数)、异常时线程退出。上图中,池化后线程初始化(thread initialize)包含线程创建、创建相关内存上下文、信号处理函数注册、内存追踪信息初始化、初始化GUC选项等操作;池化后线程轻量级/查询级初始化(light initialize)包含恢复GUC参数、初始化BackendParams、重置GUC参数等操作。stream线程池采用无锁队列实现,其目的是为了高效管理线程的出/入池操作。定义结构体ThreadSlot保存线程池中每一个线程的信息,包含:线程状态、线程号、线程对应的database oid、线程执行所需的信息StreamProducer、线程唤醒所需的锁和条件变量。当线程还未被创建时,初始化一定数量的ThreadSlot数量以预留stream线程,这些ThreadSlot被保存在数组threadSlots中。当stream线程执行完毕,需要将stream线程放置到表征可复用线程的无锁队列,称之为idleRing;当线程因为超时、异常等原因不再复用,需要退出时,将stream线程对应的ThreadSlot放置到表征未创建线程的无锁队列,称之为emptyRing。idleRing的作用是为了快速获取并复用线程池中的线程,emptyRing的作用是快速获取一个未被使用的ThreadSlot结构,以创建一个新的stream线程。由于stream线程的初始化信息和database是强相关的,如果不保留database相关的信息,那么线程初始化的时间代价仍然较高,所以线程池中的线程复用时,需要满足database信息匹配。对于设计线程池而言,每一个database都应该对应一个idleRing。综上所述,基于无锁队列的stream线程池设计如下所示:从上图可以看出,一个线程池包含预留stream线程结构的threadSlots、一个表征未创建线程的无锁队列emptyRing和表征可复用线程的无锁队列idleRing,由于每个database对应一个idleRing,因此多个idleRing被组织为链表结构。本文介绍了GaussDB(DWS)线程池总体设计思想和实现方案,更细节的状态转移和接口将在后续介绍。
  • [集群&DWS] GaussDB(DWS)stream线程池设计(四)
    一、 stream线程池实现设计Stream线程池中stream线程整体执行流程如下图所示:Stream线程池中stream线程整体执行流程如下图所示:图一、stream线程执行流程其中,GUC参数的设置逻辑为图八所示:父线程保存自己的guc_variables在syncGucVariables中,syncGucVariables是需要传递给stream的结构用以保证父子线程guc参数的一致。然后父线程在初始化streamProducer时将syncGucVariables保存在该结构中传递。Stream线程根据streamProducer初始化自己的syncGucVariables变量,首先reset所有的guc变量,然后根据syncGucVariables修正自己的variables。图二、GUC参数修改执行计划时,stream线程需要可视父线程的事务信息,因此在ExecuteStreamPlan中,stream线程池前后事务的继承和提交逻辑为:图三、事务的继承和提交逻辑二、测试场景【场景一】集群基础行为场景——建立多数据库场景Create database ***;(建立多库)分别执行带stream算子的查询;例:create table test_01(c1 int, c2 int)with(orientation=column) distribute by hash(c1);insert into test_01 select generate_series(1,100), generate_series(1,100);analyze test_01;select * from test_01 a, test_01 b, test_01 c, test_01 d, test_01 e, test_01 f where a.c2 =b.c2 and c.c2 = d.c2 and e.c2=f.c2 limit 100;查询结束,查pgxc_thread_wait_status看DN节点:预期stream线程状态为wait thread cond。且多database之间stream线程不复用。【场景二】集群基础行为场景——建立多用户场景Create user ***;(建立多用户)分别执行带stream算子的查询;(参考场景一示例)查询结束,查pgxc_thread_wait_status看DN节点:预期stream线程状态为wait thread cond。且多user之间stream线程可以复用。例:用户一执行完查询,视图中显示共有四个stream线程在线程池,用户二执行同样查询返回正确结果,视图中的stream线程个数不变,且线程号也是一致的,则说明复用。【场景三】集群基础行为场景——线程清理场景调整guc参数max_stream_pool的值,观测是否生效;预期:当设置max_stream_pool小于当前idle线程个数,支持线程个数实时减少;当设置max_stream_pool大于当前idle线程个数,将由业务驱动线程个数的增加,但是不会超过max_stream_pool。执行clean connection(ALL force),查看stream线程是否被清理;预期:该database的stream线程被完全清理。执行drop database命令,查看stream线程是否被清理;预期:该database的stream线程被完全清理。【场景4】集群基础行为场景——stream线程池性能测试分别测试50、100、300、500、1000并发下,每并发100查询的QPS,对比stream线程池性能变化。单语句stream多,并发小;单语句stream少,并发大两种场景。1.1.2  扩展场景【场景一】集群故障场景Kill dn 节点,节点被重新拉起,继续并发执行查询;集群规模性重启,并发执行查询;故障场景测试用例下,并发执行带stream算子的查询。预期:结合pgxc_thread_wait_status视图,并发查询能否继续执行,是否正常报错,无core无hang。【场景二】集群特殊业务场景模拟高并发查询,观察stream线程情况;震荡模型下,观察stream线程大规模快速建立、清理的场景是否正常运行;多用户共享一个database,多用户共享多个database,观察stream线程复用情况。预期:stream线程池正常发挥作用,不同database线程不复用,复用与user无关。
  • [集群&DWS] GaussDB(DWS)stream线程池设计(三)
    一、外部接口1.1 新增接口介绍stream线程池新增GUC参数max_stream_pool,用于控制线程池中最大可用线程的个数。默认值:65535(整数最大值)取值范围:-1~INT_MAX;-1表示不开启stream线程池max_stream_pool支持reload更新,更新规则:设置max_stream_pool小于当前可用线程个数,支持线程个数实时减少;当设置max_stream_pool大于当前idle线程个数,将由业务驱动线程个数的增加。1.2  修改接口介绍stream线程池修改pg_thread_wait_status视图中等待状态,新增wait stream cond状态表示线程池中等待被复用的stream线程,DN上可见。图一 pg_thread_wait_status视图pg_comm_status视图补充三个变量:复用stream线程的次数、使用stream线程的次数(含复用和create)、stream线程并发个数的历史峰值。performance显示是否复用线程,拿连接唤醒等开销性能数据,超出1ms输出到performance。二、  内部接口Stream线程池提供了三种接口以管理stream线程,除外部接口外,内部的接口包含被动清理接口和超时清理接口。如图六所示:图二、stream线程管理接口被动清理接口CleanStreamPool负责根据database清理stream线程,其提供三种清理模式:CLEAN_QUARTER_FREE:清理四分之一idle线程;CLEAN_ALL_FREE:清理所有idle线程;CLEAN_ALL_FORCE:清理所有dababase相关的stream线程。该接口由内部函数调用,例如dropdb、clean connection等。自动清理接口负责自行定时清理超时未使用的idle线程,其不开放给任何调用,此处接口可以理解为线程的一种管理方式。清理逻辑:max_stream_pool阈值不作强约束,尽量缓存线程,通过空闲连接利用率决定回收间隔,空闲率高时快速回收,反之慢速回收。三、接口实现逻辑接口的实现逻辑:3.1、被动清理接口int StreamThreadPool::CleanStreamPool(const char *dbName, cleanOption cleanMode)入参:dbName cleanMode返回值:清理的个数CleanMode可选:CLEAN_QUARTER_FREE、CLEAN_ALL_FREE、CLEAN_ALL_FORCE实现流程如下: 图三、CleanStreamPool接口执行逻辑3.2、超时清理接口超时清理接口是指idle状态的slot超时未被使用而自动清理,其实现在图七的wait()模块,对应StreamThreadPool::Wait()函数,其实现流程如图十一所示:可以看出当线程超时需要清理时,实际对应的操作为:将slot的状态从IDLE置为HOLD,表示该slot已被预占作为退出线程。随后返回false,函数退出。根据图七所示,wait()返回false后,stream线程会退出,调用回调函数StreamQuitAndClean(),将slot归还emptyRing,其执行逻辑如图十二所示。由图十一和十二可以看出,当slot超时退出,此时slot的状态仅仅被修改为了EXIT,而其所处的位置仍然在idleRing中,slot此时在idleRing中等待被pop,pop后发现状为EXIT,会将slot放回置empty状态中,pop的逻辑如图十二所示。  图四、wait()执行逻辑图五、slot异常退出执行逻辑  图六、slot获取执行逻辑外部接口stream线程池可通过reload参数max_stream_pool,用于控制线程池中最大可用线程的个数。具体流程如下:图七、外部guc set逻辑
  • [集群&DWS] GaussDB(DWS)stream线程池设计(二)
    一、数据结构设计stream线程池采用无锁队列实现,其目的是为了高效管理线程的出/入池操作。定义结构体ThreadSlot保存线程池中每一个线程的信息,包含:线程状态、线程号、线程对应的database oid、线程执行所需的信息StreamProducer,StreamProducer是父线程向子线程传递的唯一结构、线程唤醒所需的锁和条件变量。如下所示:typedef struct{       int status;       uint32 idx;       ThreadId tid;       Oid dbOid;       StreamProducer* streamObj;       pthread_mutex_t m_mutex;       pthread_cond_t m_cond;} ThreadSlot;定义结构体StreamThreadPool表征线程池,结构如下所示:class StreamThreadPool: public BaseObject{public:    StreamThreadPool();    void Init(int num);                             // StreamThreadPool init       int Call(StreamProducer* obj);                   // 获取idle线程 或 create 新线程    bool Wait();                                  // idle线程等待唤醒或者超时退出    ThreadSlot* GetLocalSlot();                     // get streamThreadSlot    void SetLocalSlot(int slotIdx);                   // set streamThreadSlot    StreamPool* GetLocalPool();                    // 获取streamDBPool 或 新建一个    ThreadSlot* PopSlot();                         // 从idleRing/emptyRing获取一slot    void PushToEmpty(ThreadSlot* slot);             // 将slot直接放入emptyRing    void PushToIdle(StreamPool* pool, ThreadSlot* slot); //将slot直接放入idleRing    void LocalPushToIdle();                        // 根据状态,将slot放入idleRing    void LocalPushToEmpty();                      // 根据状态,将slot放入emptyRing    int CleanStreamPool(const char *dbName, cleanOption cleanMode); //根据db信息清线程    void CleanInAllStreamPool(int desNum);           // 调整线程池中stream线程个数    int GetStreamNum();                           // 获取线程池中stream线程个数    bool Release();                                // 判断超时线程是否需要清理    bool TimeoutClean();                                // 清理超时idle线程private:    int size;    ThreadSlot* threadSlots;    ArrayLockFreeQueue emptyRing;    StreamPool* PoolListHead;}结构体中,size表示线程池中拟预留的ThreadSlot个数,ThreadSlot被保存在threadSlots数组中;无锁队列emptyRing用来保存未创建线程的ThreadSlot,对应地,idleRing用来保存空闲的已创建stream线程的ThreadSlot。由于stream线程的初始化信息和database是强相关的,如果不保留database相关的信息,那么线程初始化的时间代价仍然较高,所以线程池中的线程复用时,需要满足database信息匹配,所以一个emptyRing和一个database相匹配,保存在链表PoolListHead中,链表元素StreamPool对应结构如下:typedef struct StreamPool{       Oid dbOid;       ArrayLockFreeQueue idleRing;       struct StreamPool* next;} StreamPool;线程池中各结构间组织的直观图如下所示:图一、基于无锁队列的线程池结构上图中threadSlots可以放在idleRing(蓝色)、emptyRing(绿色)和运行空间(黄色)中,具体在下节介绍。二、stream线程状态转移DFA设计每一个记录线程信息的结构ThreadSlot中都保存了线程当前的状态status,记录线程状态的目的是为了保障线程执行过程的有序控制,也可以通过状态的互斥避免threadSlot不会被两个线程同时使用。stream线程状态转移用确定性有限状态机(DFA,definite automata)表征,共包含4个状态:STREAM_SLOT_EXIT、STREAM_SLOT_IDLE、STREAM_SLOT_HOLD和STREAM_SLOT_RUN状态。其物理含义如下:STREAM_SLOT_EXIT:线程退出状态,表示线程未被创建或线程已退出;STREAM_SLOT_IDLE:线程可复用状态,表示线程在idleRing中,可以被复用;STREAM_SLOT_HOLD:线程临时独占状态,表示线程在做进入下一个状态的准备工作;STREAM_SLOT_RUN:线程运行状态,表示线程正在执行任务。状态间转移条件如下所示,图中粗箭头表示状态机主循环部分:图二、stream线程状态转移与状态对应的,是slot所处的位置,slot所处的位置有三处,分别是idleRing、emptyRing和运行空间,slot从无锁队列中拿出,运行时所处的位置,我们称之为运行空间。各状态所处的位置情况如下所示:STREAM_SLOT_EXIT:idleRing(idle线程超时)、emptyRing(初始化或者FATAL);STREAM_SLOT_IDLE:idleRingSTREAM_SLOT_HOLD:运行空间(从无锁队列中取出)、idleRing(idle线程超时或中断);STREAM_SLOT_RUN:运行空间。Slot的位置变化和状态转移的关系如下,图中粗箭头表示状态机主循环部分:图三、stream线程状态转移和slot位置的转移关系根据各状态所处的位置情况,从idleRing中取出的slot可能有三种状态:EXIT、IDLE、HOLD。当取出IDLE状态的slot,说明线程可复用;当取出EXIT状态的slot,说明线程已退出,此时需要将slot转存到emptyRing;当取出HOLD状态,说明线程正在被使用,此时需要放回idleRing。EmptyRing中slot的状态只能是EXIT,运行空间中slot的状态要么是HOLD(刚取出还未运行),要么是RUN(正在运行),不再赘述。
  • [集群&DWS] GaussDB(DWS) 集群通信系列三:集群通信常用视图
    视图是检测数据库运行状态的重要工具和手段,GaussDB(DWS)集群通信常用视图主要包含pg_stat_activity、pg_comm_client、pgxc_thread_wait_status、pgxc_comm_recv_stream、pgxc_comm_send_stream、pgxc_comm_status,其在数据库通信问题定位中发挥了重要作用。1、pg_stat_activity该视图显示和当前用户查询相关的信息。主要用于查看当前用户执行查询的状态和查询对应的query_id,2、pg_comm_client_info该视图存储单个节点客户端连接信息(DN上查询该视图显示CN连接DN的信息)。3、pgxc_thread_wait_status该视图显示由执行语句产生的线程之间层次调用关系,以及各个线程的阻塞等待状态。该视图常用来定位数据库通信过程中的hang问题,主要用于定位的信息包括:Ø  query_id:查询IDØ  tlevel:线程层级,对于集群通信而言,Postgres thread线程为0级线程,其根据任务会fork其他子线程,集群通信线程间的关系具体可以参考http://3ms.huawei.com/hi/group/2191/wiki_5275953.html?for_statistic_from=all_group_wikiØ  wait_status:等待线程的当前等待状态,none表示没有等待,其他状态可参考产品文档。4、pgxc_comm_recv_stream该视图显示DN上所有的通信库接收流的状态。5、pgxc_comm_send_stream该视图显示DN上所有的通信库发送流的状态。6、pgxc_comm_status该视图显示所有DN的通信库状态。包含:节点名称、节点通信库发送/接收速率、cmailbox的buffer大小、libcomm/libpq进程通信内存大小、线程实时/最高实时使用率、当前使用的逻辑连接总数。通信视图的关联关系使用和hang问题定位示例查询单个的视图往往信息有限而作用不大,视图最大的妙用在于利用各信息的关联关系,要求使用者能够联系多方信息,进行问题定位和状态监测,这一点无疑比较困难,因此整理和介绍视图的关联关系对于视图使用者极为重要,此处仅通过hang问题的示例,和视图部分信息的关联关系使用,介绍这种运用思想和方法,通信视图完整的关联关系将在后续GaussDB(DWS) 集群通信系列中给出。可以看出,定位hang问题的一般步骤为:1、  根据pg_stat_activity视图查看当前查询的query_id。2、  根据pgxc_thread_wait_status中相应的query_id查询对应的线程状态,是等待什么导致的hang3、  根据wait_status的信息,查看对端信息,包含线程层级,节点等4、  查看对端具体的线程信息pg_thread_wait_status,如果是DN可查对应的连接流信息,可以利用netstat,线程号查看对端信息,分析对端的行为。通信hang问题典型案例:
  • [技术干货] Cassandra copy命令使用指南
    copy是cqlsh中的逻辑导入导出命令。包括两个命令COPY TO / COPY FROM.使用这组命令可以在Cassandra与其他RDBMS或Cassandra之间迁移数据。目前已经支持csv, json文件格式以及标准输出和输入。 1. 如何执行copy?    1. 执行copy时,首先要启动cqlsh. 启动cqlsh的方法见 如何使用cqlsh访问Cassandra。启动cqlsh后,接着执行copy命令。如下1. /cqlsh  127.0.0.1 9042 -u {user_name} -p {password}1COPY cycling.cyclist_name TO  '/home/cas/copydata' ;2. 如果数据量很大,可以放在后台执行。使用cqlsh的-e 参数。-e 参数用来后台执行给定的语句,然后退出。命令如下1. /cqlsh  127.0.0.1 9042 -u {user_name} -p {password} -e  "COPY cycling.cyclist_name TO '/home/cas/copydata'" ;2. COPY TO   1. /cqlsh  127.0.0.1 9042 -e  "COPY cycling.cyclist_name to '/home/cas/copydata'"COPY TO 常见的参数有: NUMPROCESSES, RATEFILE, PAGESIZE, BEGINTOKEN, ENDTOKEN, MAXATTEMPTS, MAXOUTPUTSIZE; 新增的参数有: RESULTFILE, DATAFORMATS, WHERECONDITION.重点介绍常见和新增的参数,关于其他COPY TO的参数可以参考Cassandra官网文档: copy-to1. `file name` 导出时,可以指定文件名称为目录, 也可以指定文件名称为文件。默认导出到文件。如果指定导出文件为存在的目录时,将会按照range范围导出到目录下的不同的文件中,如果某一个range范围导出没有数据,则不会生成文件。如果指定的文件名是文件,那将会导出到指定的文件中。如果文件不存在,则新创建该文件。1. /cqlsh  127.0.0.1 9042 -e  "COPY cycling.cyclist_name to '/home/cas/copydata'"1. /cqlsh  127.0.0.1 9042 -e  "COPY cycling.cyclist_name to '/home/cas/copydata/cycling.cyclist_name'"2. `NUMPROCESSES` 导出的线程数。在导出时,会将range范围进行细分。导出的线程数越多,细分range的范围数也就越多。但当线程数太多的时候,容易对服务端造成压力,造成导出失败。所以要选择一个合适的线程数进行导出,默认的线程个数是(CPU核数 - 1)3.  `RATEFILE` 速率文件。指定文件路径后,会打印导出过程的瞬时速率,可以用来评价导出的性能。4.  `PAGESIZE` 在一次page查询中。获取的row数。 默认为 1000。 建议不要设的调小。设置太小会影响导出性能。5.  `BEGINTOKEN, ENDTOKEN` 执行要导出的range范围。默认是全部数据导出。6.  `MAXATTEMPTS` 每一个查询重试的次数。如果某次查询达到最大重试次数,那么导出将会直接失败。 7.  `MAXOUTPUTSIZE` 每一个导出文件中的最大的行数。如果导出数据大于这个值。将会轮转生成另一个文件。默认是不限制的。1. /cqlsh  127.0.0.1 9042 -e  "COPY cycling.cyclist_name to '/home/cas/copydata/cycling.cyclist_name' with MAXOUTPUTSIZE=1"8.  `RESULTFILE` 导出结果文件路径,如果不设置。默认生成在当前执行目录下。如果执行目录中存在之前的结果文件,将会重命名之前的结果文件。导出结果的内容主要包括:导出成功与否,总共导出的行数和速率,导出的range范围数,以及成功失败的range范围数,并展示每一个range范围的结果和导出的行数。9. `DATAFORMATS` 导出的数据格式。取值为csv, json。默认为csv格式。指定为json时,导出为json格式的数据。10. `WHERECONDITION` 导出的查询条件。支持查询条件的导出。对于非主键列,如果通过索引来查询,提高导出性能。         1. wherecondition 格式如下 `[key(operators)value,key(operators)value...]`        2. operators 支持 `[">=", "<=", ">", "<", "="]`,例如: `'keyspace_name=system,table_name>=local'`        3. 如果value中包含有特殊字符,比如保留字符 【" ,><=’】, 那么需要给字符加上双引号,例如:`'keyspace_name=system,table_name>="loc>=al"'`一个完整的COPY TO例子:1nohup  . /cqlsh  127.0.0.1 9042 --request-timeout=3600 --debug -e  "COPY nihao.sz_user to '/home/cas/copydata' with WHERECONDITION='update_timestamp=1' NUMPROCESSES=12 AND RATEFILE='rate.txt' AND RESULTFILE='export_result' AND dataformats='json';"   > export .log 2>&1 &3. COPY FROM   1COPY <table name> [(<column>, ...)] FROM < file  name> WITH <copy option> [AND <copy option> ...]COPY FROM 常见的参数有: NUMPROCESSES, MAXROWS, INGESTRATE, ERRFILE, MAXBATCHSIZE, MINBATCHSIZE, CHUNKSIZE, MAXPARSEERRORS, MAXINSERTERRORS, SKIPROWS, SKIPCOLS;新增的参数有: DATAFORMATS.重点介绍常见和新增的参数,关于其他COPY FROM参数可以参考Cassandra官网文档: copy-from1. `file name` 导入数据时,指定导入的文件路径,可以是一个目录,或者是一个文件,或者是逗号分隔的文件名列表。如果是目录,则导入该目录下的所有文件。2. `NUMPROCESSES` 导入的线程数3. `MAXROWS` 导入的最大行数。默认不限制。4. `INGESTRATE` 每一秒导入的最大行数。默认为 1000005. `ERRFILE` 导入失败的列将放在这个文件中。6. `MAXBATCHSIZE` 每一次batch导入的最大行数。默认为207. `MINBATCHSIZE` 每一次batch导入的最小行数。默认为28. `CHUNKSIZE` 导入主线程给子线程每次传递的行数,默认为 10009. `MAXPARSEERRORS` 可以忽略的语法解析错误的最大行数。默认不限制10. `MAXINSERTERRORS` 可以忽略的插入失败的最大行数。默认为100011. `SKIPROWS` 导入时初始跳过的行数。默认为0,不跳过12. `SKIPCOLS` 导入时忽略的列名,以逗号分隔。默认不忽略。13. `DATAFORMATS` 导入的数据格式。取值为csv,json。默认为csv格式。数据为json格式时,必须指定为json。一个完整的COPY FROM例子:1nohup  . /cqlsh  127.0.0.1 9042 --request-timeout=3600 --debug -e  "COPY nihao.sz_user FROM '/home/cas/copydata' with NUMPROCESSES=12 AND RATEFILE='rate.txt' AND dataformats='json';"   > import .log 2>&1 &
  • [其他] 【扩容】重分步进程意外退出
    问题现象:重分步进程意外退出检查是否有会话:select coorname, usename, client_addr, query_start,sysdate-query_start as dur, state, enqueue,waiting, pid,query_id, substr(query,1,200),datname from pgxc_stat_activity where application_name = 'gs_redis' and state = 'active';恢复方案:python命令拉起,在主cn上执行/opt/dws/package/script/gs_expand -t redistribute --fast-redis --redis-mode=insert --dws-mode --parallel-jobs=24 > gs_expand.log 2>&1 &
  • [问题求助] 【Atlas200】【5.0.3 alpha推理】偶发的segment fault
    atlas200 5.0.3推理会有偶发的segment fault, 大概运行20次左右出现一次,经过我们排查定位发现只需要最简单的初始化,什么都不做,然后析构掉就会在进程的main结束之后(经过gdb debug发现main已经结束了在某个线程中)在某个后台线程中触发segment fault,我们的代码没有多线程所以我怀疑是不是默认的context中有多线程处理问题;麻烦帮忙排查;【功能模块】析构:一些日志:【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
总条数:756 到第
上滑加载中