我有一个具有 16 个核心和 8Gb RAM 的 R 服务器,它初始化一个由 10 个工作线程组成的本地 SNOW 集群。每个工作人员从 Microsoft SQL 服务器下载一系列数据集,将它们合并到某个键上,然后对数据集运行分析,然后将结果写入 SQL 服务器。 Worker 和 SQL Server 之间的连接通过 RJDBC 连接运行。当多个工作人员从 SQL 服务器获取数据时,内存使用量会激增,R 服务器会崩溃。
奇怪的是,与加载的数据集的大小相比,加载数据的工作人员使用的内存似乎不成比例地大。每个数据集大约有 8000 行和 6500 列。当在磁盘上保存为 R 对象时,这意味着大约 20MB;当保存为逗号分隔文件时,大约为 160MB。然而,R session 的 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>")
在此之后,有一些代码对函数输入向量 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)
然后我们遍历 requestDatasets 向量并加载和/或合并数据:
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)
当我在一系列十二个数据集上运行此代码时,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 服务器端的数据,但后来遇到了列数限制。
令我烦恼的是,RAM 使用量比最终创建的数据集大得多,让我相信存在某种缓冲区/堆溢出......但我似乎无法找到它。
任何有关如何解决此问题的建议将不胜感激。如果我的问题描述(部分)含糊不清或者您需要更多信息,请告诉我。
谢谢。
最佳答案
这个答案更像是一个美化的评论。仅仅因为一个节点上处理的数据只需要 160MB,并不意味着处理它所需的内存量就是 160MB。许多算法需要 O(n^2)
存储空间,对于您的数据 block 来说,该空间以 GB 为单位。所以我实际上没有看到任何不足为奇的东西。
I've already tried a couple of fixes like calling GC(), JGC() (which is a function to force a Java garbage collection...
你不能在 Java 中强制进行垃圾回收,只能礼貌地调用 System.gc()
要求 JVM 进行垃圾回收,但它可以自由地进行垃圾回收。如果需要,请忽略该请求。无论如何,JVM 通常会自行很好地优化垃圾收集,我怀疑这是您的瓶颈。更有可能的是,您只是利用了 R 处理数据所需的开销。
关于sql-server - 并行计算环境中使用JDBC时服务器内存不足问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40611525/