2011-12-29 73 views
1

我们有一个定期读取CSV文件并将记录插入Oracle 11g数据库的ColdFusion 9脚本。 CSV文件大约有50列,其中8个由CF使用(CSV格式不能修改)。该CFM的一般流程是:读取用于数据库导入的csv文件时ColdFusion内存尖峰化

  1. 读取使用该变量作为与CHR作为分隔符的列表属性(13)文件导入可变
  2. CFLOOP
  3. 呼叫Oracle存储过程中插入来自各个值使用ListGetAt

存储过程文件执行下列操作:

  1. 插入具有2网络连接的记录视场为表1
  2. 插入8个领域(包括表1的主键)为表2
  3. 没有返回值

这成功地运行在大多数情况下,阅读数以百计的记录400页KB的文件记录只需几秒钟。但是,偶尔我们会得到一个大容量,并且最终创建一个13k创纪录的5MB文件。当我们尝试处理一个这样大的文件时,我观察到JVM的内存使用量在10-15秒内从90MB增加到680MB左右,之后CF服务器监视器停止响应(CF也如此),迫使我们重新启动服务。日志报告JVM内存不足错误:

"Error","qtp4795249-38798","12/28/11","16:29:20",,"GC overhead limit exceeded" java.lang.OutOfMemoryError: GC overhead limit exceeded

我们的JVM堆大小目前是768MB。我还没有尝试过增加它,因为即使这样做确实解决了这个问题,它将来也无法保护我们,而服务器的其他正常负载并不需要那么多。而且,我对使用JVM设置需要重启才能在生产环境中生效的做法感到犹豫不决。

这很难测试,因为导入过程运行正常,几乎没有任何明显的内存负载在我的本地开发机器和我们的QA盒子上,但是这两者与数据库的连接速度要慢得多,需要10-15分钟去完成。

我会很感激任何想法,特别是关于内存的去向。我无法弄清楚5MB数据如何变成700MB的数据。我们打开了调试信息,但调用脚本的IP地址不在调试列表中,我使用了一个cfsetting标签来关闭此页面的调试功能。以前,有一个步骤1.5将CSV数据转换为ColdFusion查询,但我为了提高效率而将其删除。两种方式都会导致错误。

+0

感谢所有的回应。由于客户发生的月末和年终处理,以及他们昨天对CF服务器持续30秒的响应,我们将等待一周左右,然后尝试实施或测试任何这些建议。然后我会更新这个问题。 – Nicholas 2011-12-30 18:49:05

回答

1

你有没有考虑直接进口到数据库?对于MySQL,这是LOAD DATA INFILE,对于SQL Server它是BULK INSERT。如果你需要一些额外的处理,比可能的方法将数据加载到临时表,然后用CFML处理它,这可以很容易地批量进行处理。

+0

谢谢你的建议。我们确实考虑过这个问题,但是我们的数据库管理员目前处于非常好的状态,管理层希望在尝试从头开始编写新的数据库导入之前尝试在ColdFusion中快速解决此问题。 – Nicholas 2011-12-29 20:40:05

1

在开始处理文件之前,不要将整个文件读入内存,而是一次读取一行文件内容。本纳德尔(当然)有一个很好的博客文章讨论这个技术:

Reading In File Data One Line At A Time Using ColdFusion's CFLoop Tag Or Java's LineNumberReader

另请参见livedocs for cfloop,特别是文件attribtue:

+0

这些都是好想法。我会研究这些并报告回来。谢谢。 – Nicholas 2011-12-29 20:40:25

+0

本的一些好东西,但让某人离开这个网站得到他们的答案不是很好。你至少可以解释本说什么吗? – ale 2011-12-30 13:44:00

+0

我的建议没有比cfloop file =“foo.txt”更多的了。我还应该包括什么? – 2011-12-30 15:59:14

0

我们有一个CF应用程序,它导入房地产MLS列表并遇到类似的问题。我们所使用的核心文件正在推送100MB,并且读取它并立即进行循环创建了许多问题。我们结束了几件事:

  1. 将文件拆分成块。导入过程使用cygwin的split.exe实用程序将文件拆分为4,000行块。然后,我们使用CFDIRECTORY获取块的列表并逐个处理它们。

  2. 对于每个块,我们将其读入,然后将文件内容拆分为一个数组(使用带有chr(13)作为分隔符的listToArray())。

  3. 我们从1循环到arrayLen(chunkArray),而不是直接遍历文件内容。这比速度更快。在那个循环中,我们也将每一行分解成一个数组。我们发现这样做并以thisRow [i](其中i是文件中的列号)访问值比反复调用listGetAt()快得多。我们正在导入的文件有90多列。

  4. 我们增加了JVM内存容量。我们的服务器非常繁忙,因此增加了一些开销。我们最终推动JVM达到32位服务器(约2GB)的高度,以便在需要时提供内存。

0

也看看没有循环查询。执行查询花费的最多时间只是建立数据库连接。

任何时候我必须从一个文件中这样做多次插入,我创建每个SQL插入语句并将它们保存在一个由分号分隔的变量中。然后,我每100次陈述就一次执行所有这些陈述。

我不得不通过这样做来重写另一位程序员的某些程序,并且能够将加工时间减少90%。这是在版本6中,所以连接共享可能会改善这一点。

+0

这也是我的第一个想法。不幸的是,数据库连接在单个查询中不接受多个语句,我不相信这是我可以改变的。 – Nicholas 2012-01-03 13:37:31

+0

它并不是多个查询之一,它只是一个接一个地运行它们,就像你在SQL PLUS中运行它们一样,都用分号分隔。我不太确定数据库如何限制这一点。 – Limey 2012-01-03 19:17:52

+0

您是否问过在一个用分号隔开的cfquery块中放置几个​​insert语句?这可以并且受用于数据库连接的驱动程序的限制。我们正在使用内置的Oracle JDBC驱动程序,我不确定如何以这种方式进行配置。我知道在我的家庭服务器上使用MySQL时,在驱动程序配置中有一个复选框允许/禁止这个;在我想出来之前,让我头痛了好几天。 :P – Nicholas 2012-01-04 13:20:01