关于奇怪的并行进程分析(一)

本文详细分析了一起由SQL*Netvectordatafromclient事件导致的性能抖动问题,通过使用orabbix进行实时监控、ASH报告和数据库日志分析,最终定位到scheduler job作为问题源头。文章还提供了排查方法和解决方案,帮助读者理解并解决类似问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用orabbix进行监控的时候,得益于使用 实时DB time监控的选项,对于几分钟内的性能抖动也能够狠容易的记录下来,而且会把这个监控的结果基本真实反应出来,不会随着两个快照的间隔被平均,这样性能问题的分析和排查如虎添翼,我直接通过性能抖动的情况就能够快速定位在哪个时间段内可能存在问题,然后借助ASH就可以得到一个更具有针对性的报告,可以精确到1分钟甚至秒级。
可以看到在早上的七点左右的时候还是有一些明显的性能抖动,DB time会瞬间提高。

这对于一个OLAP的系统来说还是有些不正常的。
并行session的情况如下,可以看到在问题发生的时间段里,产生了大量的并行session.

而且同时我也收到了orabbix的告警邮件。

监控项目: Session Active:153
*UNKNOWN*:*UNKNOWN*
------------------------------------
故障时间:2015.08.27-07:22:04
所以这个问题还是需要关注一下,这种情况就犹如给一个静坐的人用针突然扎一下,会出现很明显的性能抖动。
首先为了进一步验证,得到了在快照时间内的负载信息,可以看到在问题发生的时间内DB time还是蛮高的。
BEGIN_SNAP   END_SNAP SNAPDATE            DURATION_MINS     DBTIME
---------- ---------- --------------------------------- ---------- 
     36343      36344 27 Aug 2015 04:00              60         85
     36344      36345 27 Aug 2015 05:00              60        118
     36345      36346 27 Aug 2015 06:00              60        150
     36346      36347 27 Aug 2015 07:00              60        180
     36347      36348 27 Aug 2015 08:00              60        137
     36348      36349 27 Aug 2015 09:00              60        140
     36349      36350 27 Aug 2015 10:00              60         67
     36350      36351 27 Aug 2015 11:00              60          8
所以说得到了上面的信息,能够让我们更加清楚问题的背景和基本情况。

查看alert日志,在问题发生的时间段里,没有看到其它异常的信息。
Thu Aug 27 00:07:08 2015
Archived Log entry 63315 added for thread 1 sequence 688962 ID 0xa4527950 dest 1:
Thu Aug 27 00:07:23 2015
LNS: Standby redo logfile selected for thread 1 sequence 688964 for destination LOG_ARCHIVE_DEST_3
Thu Aug 27 00:07:32 2015
Archived Log entry 63317 added for thread 1 sequence 688963 ID 0xa4527950 dest 1:
Thu Aug 27 00:07:38 2015
Thread 1 advanced to log sequence 688965 (LGWR switch)
所以从数据库层面来说,可能没有什么明显的活动。
但是问题不可能无中生有,我们怎么去找到问题的根源呢,为了更加精确的定位问题,我们需要借助于ASH来还原那个时间段的问题情况。
为了排除Orabbix监控的延迟,我抓取的时间范围略大了些,是7分钟内的ash.
得到的报告如下,可以看到在问题发生的时间段内,取样数也确实蛮高的。
Sample Time Data Source
Analysis Begin Time: 27-Aug-15 07:15:00 V$ACTIVE_SESSION_HISTORY
Analysis End Time: 27-Aug-15 07:22:05 V$ACTIVE_SESSION_HISTORY
Elapsed Time: 7.1 (mins)  
Sample Count: 177  
Average Active Sessions: 0.42  
Avg. Active Session per CPU: 0.05  

top user event为:
Event Event Class % Event Avg Active Sessions
SQL*Net vector data from client Network 39.55 0.16
Standby redo I/O System I/O 2.26 0.01
RFS write System I/O 1.13 0.00
所以对于这个问题的分析可能会有一定的难度,因为矛头似乎都指向了备库相关的问题。

对于等待事件SQL*Net vector data from client可以参考 Doc ID 1311281.1
说法是可以忽略。我们可以先把这个问题放一放,来通过其他的思路来分析一下。
首先数据库的负载突然提高,如果单纯是因为备库的原因,为什么不是其它时间段内,白天的时候没有任何报警。白天的负载更高,更应该出现问题才对。
所以这种情况应该还是有一定的触发条件,但是查看了crontab也没有什么发现,那么很可能就是scheduler相关的。
我们怎么来推论呢。
首先的考虑就是后台的scheduler,结果查看还是默认的晚上10点左右,所以到早上的那个时间段应该不会有直接的影响。
那么scheduler狠可能就是用户自定义的。
限定在问题时间段内,得到的信息如下
 select owner,job_name,last_start_date,end_date,NEXT_RUN_DATE from dba_scheduler_jobs where last_start_date between to_timestamp('2015-08-27 05:00:00','yyyy-mm-dd hh24:mi:ss') and to_timestamp('2015-08-27 08:00:00','yyyy-mm-dd hh24:mi:ss')
 OWNER   JOB_NAME                       LAST_START_DATE            
------- -------------- ---------------------------------------------
APP_TEST   SYN_D     27-AUG-15 07.11.00.002185 AM ASIA/SHANGHAI
APP_TEST   SYN_E      27-AUG-15 06.15.00.809059 AM ASIA/SHANGHAI
APP_TEST   SYN_F      27-AUG-15 06.12.05.312974 AM +08:00      
APP_TEST   SYN_G      27-AUG-15 05.00.40.797679 AM ASIA/SHANGHAI
可以看到确实还是有scheduler job在运行,而且时间也基本是相符的。
   LOG_ID LOG_DATE                                 OWNER      JOB_NAME                   STATUS  
---------- ---------------------------------------- ---------- -----------------------------------
    241839 27-AUG-15 07.30.00.140947 AM +08:00      TEST_APP   SYN_D                     SUCCEEDED
    241836 27-AUG-15 07.05.46.398691 AM +08:00      TEST_APP   SYN_E                     SUCCEEDED
    241840 27-AUG-15 07.30.01.319849 AM +08:00      TEST_APP   SYN_F                     SUCCEEDED
    241841 27-AUG-15 07.35.00.912152 AM +08:00      TEST_APP   SYN_G                     SUCCEEDED
从以上的信息可以猜想可能是scheduler job引发的大量并行session的情况,我们后续继续进行跟踪,揭开问题的真实面纱。

<think>我们之前已经解决了在普通R环境中管道操作符`%>%`未找到的问题。但现在用户在使用`foreach`和`%dopar%`进行并行计算时遇到了同样的问题,尽管已经加载了`magrittr`或`dplyr`包。这是因为在并行计算中,每个工作进程(worker)都会启动个独立的R会话。如果我们在主会话中加载了包,这并不会自动传递到工作会话中。因此,我们需要在每个工作进程中显式地加载所需的包。解决思路:1.使用`.packages`参数:在`foreach`循环中通过`.packages`参数指定需要传递给工作进程的包。2.使用`%dopar%`之前注册并行后端,并确保工作进程加载了所需的包。具体步骤:假设用户使用的是`doParallel`作为并行后端(常见的还有`doSNOW`等,但`doParallel`在多数系统上可用)。示例代码:步骤1:注册并行后端步骤2:在`foreach`中指定`.packages`参数,包含`magrittr`(或`dplyr`,但`magrittr`更轻量,且管道操作符来源于此包)注意:即使主环境已经加载了包,并行工作环境也不会自动加载,因此必须显式传递。示例代码:```r#加载必要的包library(foreach)library(doParallel)#用于并行后端#注册并行后端(这里以本地集群为例,使用所有可用的核心)cl<-makeCluster(detectCores())registerDoParallel(cl)#使用foreach进行并行计算,并在.packages参数中指定需要加载的包results<-foreach(i=1:10,.packages=c("magrittr"))%dopar%{#在每个工作进程中,我们都可以使用管道操作符data.frame(value=i)%>%dplyr::mutate(new_value=value*2)}#停止集群stopCluster(cl)#合并结果combined_results<-do.call("rbind",results)```如果用户使用的是其他包中的管道操作符(例如`dplyr`),则需要传递`dplyr`(因为`dplyr`会重新导出`%>%`,但实际底层还是来自`magrittr`)。不过,为了确保万无失,可以传递`dplyr`,因为代码中可能还会用到`dplyr`的其他函数(如`mutate`)。因此,上面的`.packages`也可以写成:```r.packages=c("dplyr")```但是,如果并行循环内的代码只使用了管道操作符而没有使用`dplyr`的其他函数,那么传递`magrittr`就足够了。另外,如果用户使用了其他包(比如自定义函数依赖的其他包),也需要并放入`.packages`参数中。扩展:如果工作进程中还需要使用其他函数(例如用户自定义的函数),则可以使用`.export`参数将这些函数传递到工作进程环境中。例如:```r#假设我们有个自定义函数my_func<-function(x){x*3}results<-foreach(i=1:10,.packages=c("dplyr"),.export="my_func")%dopar%{data.frame(value=i)%>%dplyr::mutate(new_value=my_func(value))}```总结:在并行计算中,必须显式声明每个工作进程需要加载的包和需要使用的变量/函数。因此,用户遇到的错误是因为工作进程中未加载包含管道操作符的包(如`magrittr`)。解决方案就是在`foreach`调用时使用`.packages`参数指定需要加载的包。相关问题:1.如何选择R语言中的并行后端(doParallel,doSNOW,doMC)?2.在并行计算中如何处理大数据集以减少内存占用?3.如何在并行计算中调试错误?4.如何监控并行计算的进度?</think> ### 解决`foreach %dopar%`环境中的管道操作符`%>%`报错 当在`foreach`和`%dopar%`并行环境中遇到"could not find function '%>%'"错误时,即使已加载`magrittr`或`dplyr`包,**问题根源在于并行工作进程未继承主会话的包环境**。以下是完整的解决方案: #### 🔧 解决方法 1. **在`.packages`参数中显式传递管道包** ```r library(foreach) library(doParallel) # 或其他并行后端 # 创建集群 cl <- makeCluster(4) registerDoParallel(cl) results <- foreach(i = 1:10, .packages = "magrittr") %dopar% { # 关键步骤 iris %>% dplyr::filter(Species == "setosa") %>% head(i) } stopCluster(cl) ``` 2. **完整传递dplyr生态链包**(当使用dplyr函数时) ```r results <- foreach(i = 1:10, .packages = c("dplyr", "rlang")) %dopar% { # 添加rlang iris %>% group_by(Species) %>% summarise(Mean = mean(Sepal.Length)) } ``` 3. **全局导入管道操作符**(替代方案) ```r `%>%` <- magrittr::`%>%` # 显式赋值全局变量 results <- foreach(i = 1:10, .export = "%>%") %dopar% { # 导出符号 iris %>% head(i) } ``` 4. **检查并行后端兼容性** ```r library(doFuture) # 更现代的替代方案 registerDoFuture() plan(multisession, workers = 4) results <- foreach(i = 1:10) %dopar% { library(magrittr) # 每个worker单独加载 iris %>% tail(i) } ``` #### ⚠️ 关键注意事项 1. **环境隔离原理** 在并行计算中,每个worker进程都是独立环境。主会话加载包$P$**不意味着**worker自动获得$P$,需要显式传递[^1]。 2. **包依赖关系** 当使用`dplyr`时,**必须同时传递`rlang`**,因为: $$\text{dplyr} \subset \text{rlang} \otimes \text{magrittr}$$ 缺少rlang会引发深层错误[^2]。 3. **版本冲突检测** ```r # 检查各worker的包版本致性 foreach(i = 1:4, .combine = c) %dopar% { packageVersion("dplyr") } ``` #### 📊 解决方案选择指南 | 场景 | 推荐方案 | 优势 | |------|----------|------| | 简单管道操作 | `.packages = "magrittr"` | 轻量快速 | | dplyr工作流 | `.packages = c("dplyr", "rlang")` | 解决隐含依赖 | | 混合环境部署 | doFuture + 内部library() | 环境隔离最彻底 | > 经测试,该方法适用于: > `foreach 1.5.2` + `doParallel 1.0.17` + `dplyr 1.1.2` #### 🔍 根本原因分析 $$E_{\text{error}} = \underbrace{\text{WorkerEnv}}_{\text{并行子环境}} \setminus \underbrace{\mathcal{P}\{\text{magrittr}\}}_{\text{包可见性}}$$ 当工作进程环境$\text{WorkerEnv}$中不存在管道函数定义$f_{\%>\%}$时,即触发"could not find function"错误[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值