生成json的任务中的许多查询

时间:2011-03-05 02:17:57

标签: coldfusion

所以我有一个要构建的任务,它将把我们数据库中的大量数据存档为JSON。

让您更好地了解正在发生的事情; X有100s的Ys,Y有100s的Zs,依此类推。我正在为每个X,Y和Z创建一个json文件。但是每个X json文件都有一个子数组,其中包含X的子Ys,同样Ys存储了一个子Zs数组..

它比许多情况下的情况更复杂,但你应该从我认为的那个例子中了解复杂性。

我使用的是ColdFusion,但这对于此任务似乎是一个糟糕的选择,因为它因内存错误而崩溃。在我看来,如果它正在从运行任务时不再引用的内存中删除查询(即:垃圾收集),那么任务应该有足够的内存,但是根据ColdFusion根本没有进行任何垃圾收集,并且必须在请求完成后执行此操作。

因此,我正在寻找有关如何更好地完成我在CF中的任务的建议,或者寻求有关其他语言的建议。

感谢。

2 个答案:

答案 0 :(得分:5)

1)如果启用了调试,coldfusion将继续查询,直到页面完成。把它关掉!

2)您可能需要structDelete()查询变量以允许它被垃圾收集,否则只要存在引用它的作用域,它就可能持久存在。例如。, <cfset structDelete(variables,'myQuery') />

3)cfquery将整个ResultSet拉入内存。大多数时候这很好。但是对于大型结果集的报告,您不希望这样。一些JDBC驱动程序支持设置fetchSize,它以前向只读方式允许您一次获得一些结果。这样,您可以处理成千上万行,而不会淹没内存。我只用了不到100mb的堆就在~80秒内生成了一个1GB的csv文件。这需要退出Java。但它一石二鸟。它减少了JDBC驱动程序一次带入的数据量,并且由于您直接使用ResultSet,因此不会遇到提到的cfloop问题@orangepips。当然,不适合没有Java杂志的人。

你可以这样做(你的构建路径中需要cfusion.jar):

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.sql.ResultSet;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import au.com.bytecode.opencsv.CSVWriter;
import coldfusion.server.ServiceFactory;

public class CSVExport {
    public static void export(String dsn,String query,String fileName) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        FileWriter fw = null;
        BufferedWriter bw = null;


        try {
            DataSource ds = ServiceFactory.getDataSourceService().getDatasource(dsn);
            conn = ds.getConnection();
            // we want a forward-only, read-only result.
            // you may want need to use a PreparedStatement instead.
            stmt = conn.createStatement(
                ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_READ_ONLY
            );
            // we only want to go forward!
            stmt.setFetchDirect(ResultSet.FETCH_FORWARD);
            // how many records to pull back at a time.
            // the hard part is balancing memory usage, and round trips to the database.
            // basically sacrificing speed for a lower memory hit.
            stmt.setFetchSize(256);
            rs = stmt.executeQuery(query);
            // do something with the ResultSet, for example write to csv using opencsv
            // the key is to stream it. you don't want it stored in memory.
            // so excel spreadsheets and pdf files are out, but text formats like 
            // like csv, json, html, and some binary formats like MDB (via jackcess)
            // that support streaming are in.
            fw = new FileWriter(fileName);
            bw = new BufferedWriter(fw);
            CSVWriter writer = new CSVWriter(bw);
            writer.writeAll(rs,true);
        }
        catch (Exception e) {
            // handle your exception.
            // maybe try ServiceFactory.getLoggingService() if you want to do a cflog.
            e.printStackTrace();
        }
        finally() {
            try {rs.close()} catch (Exception e) {}
            try {stmt.close()} catch (Exception e) {}
            try {conn.close()} catch (Exception e) {}
            try {bw.close()} catch (Exception e) {}
            try {fw.close()} catch (Exception e) {}
        }
    }
}

弄清楚如何传递参数,记录,将其转换为后台进程(提示:扩展线程)等是不同的问题,但是如果你理解这些代码,那就不应该太难了。

4)或许看Jackson生成你的json。它支持streaming,并结合fetchSize和BufferedOutputStream,你应该能够保持内存使用率下降。

答案 1 :(得分:4)

Eric,在请求结束和I've documented it fairly extensively in another SO question之前,您对ColdFusion垃圾回收没有从内存中删除查询信息是绝对正确的。简而言之,当您循环查询时,您会点击OoM Exceptions。您可以使用VisualVM之类的工具来证明它,以便在进程运行时生成堆转储,然后通过Eclipse Memory Analyzer Tool(MAT)运行生成的转储。 MAT将向您展示的是一个大型层次结构,从一个名为(我没有做到这一点)CFDummyContent的对象开始,其中包含对cfquery和{{1}的引用。 }标签。注意,尝试将其更改为存储过程甚至通过JDBC进行数据库交互并没有什么不同。

因此。什么。至。怎么办?

我花了一段时间才弄明白,但你有三个选项可以提高复杂程度:

  1. cfqueryparam
  2. asynchronous CFML gateway
  3. 菊花链http请求
  4. 使用cfthread看起来像这样:

    <cthread/>

    注意,此代码利用异步处理,因此每次线程调用后立即连接,而是cfthread在其自己的请求范围内运行的副作用,与页面无关。

    我不会在这里覆盖ColdFusion网关。 HTTP菊花链表示执行工作的增量,并在增量结束时启动对同一算法的请求,告诉它执行下一个增量。

    基本上,所有这三种方法都允许在进程中收集这些内存引用。

    是的,对于任何要求的人,已经提出过Adobe的错误,请参阅引用的问题。此外,我认为此问题特定于Adobe ColdFusion,但尚未测试Railo或OpenDB。

    最后,不得不咆哮。我花了很多时间跟踪这个,将它修复到我自己的大型代码库中,并且引用的问题中列出的其他几个也是如此。 AFAIK Adob​​e并没有承认这个问题,更不用说修复它了。并且,是的,它是一个简单而简单的错误。