在并行计算环境中使用RJDBC时出现服务器内存不足问题

Pre*_*016 5 memory sql-server garbage-collection r rjdbc

我有一个带有16个内核和8Gb内存的R服务器,它初始化了一个本地SNOW集群,例如10个工作者.每个工作人员从Microsoft SQL服务器下载一系列数据集,将它们合并到某个键上,然后在将结果写入SQL服务器之前对数据集运行分析.worker和SQL Server之间的连接通过RJDBC连接运行.当多个工作程序从SQL服务器获取数据时,ram使用情况会爆炸,R服务器崩溃.

奇怪的是,与加载数据集的大小相比,加载数据的工作者使用ram的用法似乎不成比例地大.每个数据集包含大约8000行和6500列.当在磁盘上保存为R对象时,这将转换为大约20MB,在保存为逗号分隔文件时大约为160MB.然而,R会话的ram使用大约是2,3 GB.

以下是代码的概述(一些提高可读性的印刷更改):

使用RJDBC建立连接:

require("RJDBC")
drv <- JDBC("com.microsoft.sqlserver.jdbc.SQLServerDriver","sqljdbc4.jar")
con <<- dbConnect(drv, "jdbc:sqlserver://<some.ip>","<username>","<pass>")
Run Code Online (Sandbox Code Playgroud)

在此之后,有一些代码将函数输入向量requestedDataSets与所有表的名称进行排序,以按记录数查询,这样我们就可以从最大到最小加载数据集:

nrow.to.merge <- rep(0, length(requestedDataSets))
for(d in 1:length(requestedDataSets)){
nrow.to.merge[d] <- dbGetQuery(con, paste0("select count(*) from",requestedDataSets[d]))[1,1]
}
merge.order <- order(nrow.to.merge,decreasing = T)
Run Code Online (Sandbox Code Playgroud)

然后我们通过requestedDatasets向量并加载和/或合并数据:

for(d in merge.order){
    # force reconnect to SQL server
    drv <- JDBC("com.microsoft.sqlserver.jdbc.SQLServerDriver","sqljdbc4.jar")
    try(dbDisconnect(con), silent = T)
    con <<- dbConnect(drv, "jdbc:sqlserver://<some.ip>","<user>","<pass>")
    # remove the to.merge object
    rm(complete.data.to.merge)
    # force garbage collection
    gc()
    jgc()
    # ask database for dataset d
    complete.data.to.merge <- dbGetQuery(con, paste0("select * from",requestedDataSets[d]))
    # first dataset
    if (d == merge.order[1]){
        complete.data <- complete.data.to.merge
        colnames(complete.data)[colnames(complete.data) == "key"] <- "key_1"
    } 
    # later dataset
    else {
        complete.data <- merge(
                         x = complete.data, 
                         y = complete.data.to.merge,
                         by.x = "key_1", by.y = "key", all.x=T)
    }
}
return(complete.data)
Run Code Online (Sandbox Code Playgroud)

当我在12个数据集的系列上运行此代码时,complete.data对象的行/列数量与预期一致,因此合并调用不太可能以某种方式炸毁使用情况.对于十二次迭代,memory.size()返回1178,1364,1500,1662,1656,1925,1835,1987,2106,2130,2217和2361.这也是奇怪的,因为最后的数据集最多162 MB ......

正如您在上面的代码中看到的,我已经尝试了几个修复,比如调用GC(),JGC()(这是一个强制Java垃圾收集的函数jgc < - function(){.jcall("java /" lang/System",method ="gc")}).我也尝试合并SQL-server-side数据,但后来遇到了列数限制.

令我烦恼的是,RAM的使用量远远大于最终创建的数据集,这使我相信存在某种溢出的缓冲区/堆...但我似乎无法找到它.

如何解决这个问题的任何建议将不胜感激.如果我的问题描述(部分)含糊不清或者您需要更多信息,请告诉我.

谢谢.

Tim*_*sen 3

这个答案更像是一个美化的评论。仅仅因为一个节点上处理的数据只需要 160MB,并不意味着处理它所需的内存量就是 160MB。许多算法需要O(n^2)存储空间,对于数据块来说,存储空间以 GB 为单位。所以我实际上在这里没有看到任何不足为奇的东西。

我已经尝试过一些修复,例如调用 GC()、JGC()(这是一个强制 Java 垃圾收集的函数......

在 Java 中你不能强制进行垃圾收集,调用System.gc()只是礼貌地要求JVM 进行垃圾收集,但如果愿意的话,可以随意忽略该请求。无论如何,JVM 通常会自行很好地优化垃圾收集,我怀疑这是您的瓶颈。更有可能的是,您只是利用了 R 处理数据所需的开销。