【问题标题】:C# SQL query blocks server memoryC# SQL 查询阻塞服务器内存
【发布时间】:2017-02-14 12:50:40
【问题描述】:

我还是个新手,我被分配了维护以前完成的代码的任务。 我有一个模拟 SQL Management Studio 的网络,例如限制删除选项,因此基本用户不会搞砸我们的服务器。 好吧,我们有一个需要一个或多个查询的函数,它工作正常,但是我们的服务器 RAM 被复杂的查询炸毁了,也许它没有那么多数据,但是它的转换 xml 和所有我仍然没有的东西了解 SQL。

这是实际功能:

public DataSet ExecuteMultipleQueries(string queries)
    {
        var results = new DataSet();
        using (var myConnection = new SqlConnection(_connectionString))
        {
            myConnection.Open();
            var sqlCommand = myConnection.CreateCommand();
            sqlCommand.Transaction = myConnection.BeginTransaction(IsolationLevel.ReadUncommitted);
            sqlCommand.CommandTimeout = AppSettings.SqlTimeout;
            sqlCommand.CommandText = queries.Trim();
            var dataAdapter = new SqlDataAdapter { SelectCommand = sqlCommand };
            dataAdapter.Fill(results);
            return results;
        }
    }

我有点迷茫,我已经阅读了许多不同的答案,但要么我没有正确理解它们,要么它们无法以任何方式解决我的问题。

我知道我可以使用 Linq-toSql- 或 Entity,我试过了,但我真的不知道如何将它们与“未知”查询一起使用,无论如何我都可以尝试进行更多研究,所以如果你认为它们会有所帮助我正在接近解决方案,无论如何,我会努力学习它。

言归正传:

调试时该函数似乎停止在dataAdapter.Fill(results),此时服务器尝试回答查询并消耗其所有RAM并自行阻塞。我该如何解决这个问题?我想可能是通过让SQL返回一定数量的数据,将其存储在某个集合中,然后继续返回数据,一直继续直到没有更多数据可以从SQL返回,但我真的不知道如何检测是否有任何数据要从 SQL 返回。

我也想过在两个不同的线程中读取和存储,但我不知道如何将一个线程中的数据存储在另一个线程异步中(如果解决了问题就更少了)。

所以,是的,我没有任何明确的东西,所以任何指导或提示将不胜感激。

提前致谢,很抱歉发了这么长的帖子。

【问题讨论】:

  • 更重要的是,你为什么要执行来自用户的任意 SQL 语句?这是非常危险的,导致“用户”搞砸您的数据库。请参阅OWASP - SQL Injection 页面。
  • 我同意 BanksySan。这是一个可怕的想法。如果您想阻止用户执行某些操作,请为此使用权限。如果有不应该编写查询的用户,您可以为他们创建报告。
  • ...另外,包装你没有处理你的 Transaction 对象。您需要将其包装在 using 中,但完全不使用它可能会更好。
  • API 有限,因此用户无法执行 DELETE 或 DROP,它是 QA 团队的内部网站。 QA 团队只能执行来自不同环境的查询,因此他们无法删除任何内容
  • 另外,为什么这个问题被否决了?我不介意投反对票,但我想要一些反馈,这样我就可以改进 SO,谢谢

标签: c# sql sql-server entity-framework linq


【解决方案1】:

您可以使用分页仅获取部分数据。

你的代码会是这样的:

dataAdapter.Fill(results, 0, pageSize);

pageSize 可以是您想要的大小(例如 100 或 250)。

您可以在thismsdn 文章中获取更多信息。

【讨论】:

  • 该方法需要一个 DataTable,我的问题是我不知道 API 将接收哪些查询或查询
  • 在你的代码中你有DataAdapter,所以你可以用页面填充它。它不依赖于查询。
  • 所以我使用了你的答案并且它有效,我对文档感到困惑,我仍然不知道它为什么有效,但它确实有效,我使用了这个 dataAdapter.Fill(results, currentIndex, pageSize, results.DataSetName); 非常感谢你们尝试过帮助了我
【解决方案2】:

为了进行调查,请尝试以下操作:

  • 启动 SQL 分析器(它通常与SSMS 一起安装,可以从 Management Studio,工具菜单中启动)
  • 确保您填写了一些过滤器(NT 用户名或至少您正在分析的数据库)。这是为了尽可能地捕获特定(即仅您的)查询
  • 包括开始事件以查看您的查询何时开始(例如RPC:Starting)。
  • 开始您的应用程序
  • 在发出查询之前启动分析器(填充适配器)
  • 发出查询 -> 您应该会在分析器中看到查询开始
  • 停止探查器以不捕获其他查询(这会增加 SQL Server 的开销)
  • 停止应用程序(在分析完成之前没有理由搞乱服务器)

  • 在 SQL Management Studio 中进行查询。我期望一个返回大量数据的 SELECT 。不要按原样运行,而是放一个TOP 来限制它的结果。例如。 SELECT TOP 1000 <some columns> from ....

如果 TOPed 选择运行缓慢,则您返回的数据过多。

这可能是由于返回了一些较大的字段,例如 N/VARCHAR(MAX)VARBINARY(MAX)。一种可能的解决方案是从初始 SELECT 中排除这些字段并延迟加载此数据(根据需要)。

如果需要,请检查这些步骤并返回您的实际查询。

【讨论】:

  • 这对我的问题没有帮助,但它有助于我分析查询如何影响服务器的内存等等。感谢您的提示!
  • 是的,您应该深入了解您实际查询的内容。接受的答案参考明确指出: DataAdapter 通过重载 Fill 方法提供了一种仅返回一页数据的工具。但是,这可能不是对大型查询结果进行分页的最佳选择,因为尽管 DataAdapter 仅使用请求的记录填充目标 DataTable 或 DataSet,但仍使用返回整个查询的资源。因此,尽管它适用于您的情况,但资源使用量仍然可能非常大。
  • 当结果存储在内存中时,问题似乎发生了,而不是来自 SQL,所以它可能不是最佳选择,但它确实有效。我会走这条路,一旦我完全理解 c#,我会回来尝试重构或找到一种新的、更优化的方式