【问题标题】:C# - DataTable Out of Memory exception in application to catch SQL Server "INSERT" eventsC# - 应用程序中的 DataTable Out of Memory 异常以捕获 SQL Server“INSERT”事件
【发布时间】:2016-06-06 20:46:38
【问题描述】:

我的任务是创建一个应用程序来监视特定表上的任何“INSERT”事件。我打算使用 SqlDependency 在数据库和 C# 应用程序之间创建通知链接,但由于安全问题,我无法做到这一点。

因此,我将我的应用程序建模如下:

这很好,但事实证明,我正在查询的 SQL 表的大小相当大。该表有近 350 万行 55 列。加载到 C# DataTable 对象时,出现内存不足异常。

internal static DataTable ExecuteQuery(string query, Dictionary<string,string> parameters = null)
    {
        try
        {
            using (SqlConnection dbconn = new SqlConnection(SQLServer.Settings.ConnectionString))
            using (SqlCommand cmd = new SqlCommand())
            {
                dbconn.Open(); // Open the connection
                cmd.CommandText = query; // Set the query text
                cmd.Connection = dbconn;
                if (parameters != null)
                {
                    foreach (var parameter in parameters) // Add filter parameters
                        cmd.Parameters.AddWithValue(parameter.Key, parameter.Value);
                }
                var dt = new DataTable();
                using (SqlDataAdapter adpt = new SqlDataAdapter(cmd)){adpt.Fill(dt);} // MY ERROR OCCURS HERE!
                dbconn.Close();
                queryError = false;
                return dt;
            }
        }
        catch(Exception ex)
        {
            queryError = true;
            EventLogger.WriteToLog("ExecuteQuery()", "Application", "Error: An error has occured while performing a database query.\r\nException: " + ex.Message);
            return null;
        }
    }

运行上面的代码时,SqlDataAdapter.Fill(dt) 的行出现以下错误

引发了“System.OutOfMemoryException”类型的异常。

有没有一种方法可以重组我的应用程序或防止 DataTable 类出现这种令人难以置信的高内存消耗? SQL 服务器似乎有足够的能力从表中选择 *,但是当我用相同的数据填充 DataTable 时,我用掉了超过 6GB 的 RAM!为什么使用 DataTable 会有这么多开销?

这是我的flowchart的链接。

【问题讨论】:

  • 您有什么理由不想在 SQL Server 中使用触发器来执行此操作?
  • 每行的平均大小是多少?将其乘以行数。 DT 会产生多少“开销”(最多)? 无论如何, SQL server 这个例子中的结果集,所以它不需要一次全部实现。另一方面,DataTables 只存在于内存中并且是“一次性加载的”。
  • 您也可以考虑 SQL 内置机制,如审计和版本控制,如果它们适用于您的 SQL 版本

标签: c# sql-server datatable out-of-memory


【解决方案1】:

我能够通过使用SqlDataReaderclass 来解决这个问题。此类允许您逐行“流式传输”sql 结果集,而不是一次将整个结果集全部带回并将其加载到内存中。

所以现在在流程图的第 5 步中,我只能查询第一行。然后在第 6 步中,我可以在以后再次查询并一次遍历新结果集一行,直到找到我开始的原始行。一直以来,我都在用新结果填充 DataTable。这完成了两件事。

  1. 我不需要一次将查询中的所有数据全部加载到本地内存中。
  2. 我可以立即获得“反向”数据集。又名...我可以获取第一次检查时不存在的新插入的行。

这正是我所追求的。这里只是部分代码:

private static SqlDataReader reader;
private static SqlConnection dbconn = new SqlConnection(SQLServer.Settings.ConnectionString);

private void GetNextRows(int numRows)
    {
        if (dbconn.State != ConnectionState.Open)
            OpenConnection();

        // Iterate columns one by one for the specified limit.
        int rowCnt = 0;
        while (rowCnt < numRows)
        {
            while (reader.Read())
            {
                    object[] row = new object[reader.FieldCount];
                    reader.GetValues(row);
                    resultsTable.LoadDataRow(row, LoadOption.PreserveChanges);
                    rowCnt++;
                    sessionRowPosition++;
                    break;
            }
        }
    }

整个班级对我来说太大了,无法在这里发帖,但需要注意的是,我的检查间隔很长,大约几天,所以我需要关闭检查之间的连接。关闭与 SqlDataReader 的连接时,您会丢失行位置,因此我需要添加一个计数器来跟踪它。

【讨论】:

    【解决方案2】:

    检查您的查询以进行选择。您可能会从数据库中获得很多行。

    【讨论】:

      猜你喜欢
      • 2018-08-06
      • 1970-01-01
      • 2012-03-09
      • 1970-01-01
      • 2022-01-12
      • 2015-12-27
      • 1970-01-01
      • 2017-09-15
      • 1970-01-01
      相关资源
      最近更新 更多