11

我有一个应用程序,我必须从数据库中获取大量数据。由于它未能获得所有这些行(接近 2,000,000 行......),我将其中断,并且每次运行 sql 查询并且每次仅获得 200,000 行。

我使用向其中输入所有数据的 DataTable(意思是 - 所有 2,000,000 行都应该在那里)。

前几轮还不错。然后它因 OutOfMemoryException 而失败。

我的代码工作如下:

private static void RunQueryAndAddToDT(string sql, string lastRowID, SqlConnection conn, DataTable dt, int prevRowCount)
    {
        if (string.IsNullOrEmpty(sql))
        {
            sql = generateSqlQuery(lastRowID);
        }

        if (conn.State == ConnectionState.Closed)
        {
            conn.Open();
        }

        using (IDbCommand cmd2 = conn.CreateCommand())
        {
            cmd2.CommandType = CommandType.Text;
            cmd2.CommandText = sql;
            cmd2.CommandTimeout = 0;

            using (IDataReader reader = cmd2.ExecuteReader())
            {
                while (reader.Read())
                {
                    DataRow row = dt.NewRow();
                    row["RowID"] = reader["RowID"].ToString();
                    row["MyCol"] = reader["MyCol"].ToString();
                    ... //In one of these rows it returns the exception.

                    dt.Rows.Add(row);
                }
            }
        }

        if (conn != null)
        {
            conn.Close();
        }

        if (dt.Rows.Count > prevRowCount)
        {
            lastRowID = dt.Rows[dt.Rows.Count - 1]["RowID"].ToString();
            sql = string.Empty;
            RunQueryAndAddToDT(sql, lastRowID, conn, dt, dt.Rows.Count);
        }
    }

在我看来,读者似乎一直在收集行,这就是它只在第三轮或第二轮抛出异常的原因。

不应该使用清理内存吗?什么可以解决我的问题?

注意:我应该解释一下 - 我别无选择,只能将所有这些行放到数据表中,因为我稍后会对它们进行一些操作,并且行的顺序很重要,我不能拆分它,因为有时我有取一些行的数据并设置为一行等等,所以我不能放弃它。

谢谢。

4

4 回答 4

17

检查您构建的是 64 位进程,而不是 32 位进程,这是 Visual Studio 的默认编译模式。为此,请右键单击您的项目,属性 -> 构建 -> 平台目标:x64。与任何 32 位进程一样,以 32 位编译的 Visual Studio 应用程序具有 2GB 的虚拟内存限制。

64 位进程没有此限制,因为它们使用 64 位指针,因此它们的理论最大地址空间为 16 艾字节 (2^64)。实际上,Windows x64 将进程的虚拟内存限制为 8TB。内存限制问题的解决方案是编译为 64 位。

但是,默认情况下,Visual Studio 中对象的大小仍限制为 2GB。您将能够创建多个组合大小大于 2GB 的数组,但默认情况下您不能创建大于 2GB 的数组。希望,如果您仍想创建大于 2GB 的数组,您可以通过将以下代码添加到 app.config 文件来实现:

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>
于 2013-06-26T14:06:13.310 回答
3

我认为只是您的内存不足,因为您的 DataTable 从您不断添加的所有行中变得如此之大。

在这种情况下,您可能想尝试不同的模式。

除了在列表(或数据表)中缓冲您的行,您是否可以简单地产生行,因为它们在到达时可供使用?

于 2012-12-24T11:23:42.370 回答
3

由于您使用的是DataTable,让我分享一个我在使用时遇到的随机问题。检查您的构建属性。我遇到了 DataTable 随机抛出内存不足异常的问题。事实证明,该项目的构建平台目标设置为Prefer 32-bit. 一旦我取消选择该选项,随机内存不足异常就消失了。

于 2016-03-23T20:32:24.000 回答
2

您将数据的副本存储到dt. 您只是存储了太多以至于机器内存不足。所以你有几个选择:

  • 增加可用内存。
  • 减少您正在检索的数据量。

要增加可用内存,您可以向机器添加物理内存。请注意,32 位机器上的 .NET 进程将无法访问超过 2GB 的内存(如果启用 3GB 切换,则为 3GB boot.ini),因此如果您希望解决问题,可能需要切换到 64 位(机器和进程)比这更多的内存。

检索更少的数据可能是要走的路。根据您要实现的目标,您可能能够对数据的子集(甚至可能在单个行上)执行任务。如果您正在执行某种聚合(例如,从数据中生成摘要或报告),您可以使用Map-Reduce

于 2012-12-24T11:26:56.317 回答