Fill(DataTable) 测试成功,生产挂起

Fill(DataTable) succeeds in testing, hangs in Production

我有一个控制台批处理应用程序,其中包含一个使用 SqlDataAdapter.Fill(DataTable) 在 table 上执行简单 SELECT 的进程。

private DataTable getMyTable(string conStr)
    {
        DataTable tb = new DataTable();
        StringBuilder bSql = new StringBuilder();
        bSql.AppendLine("SELECT * FROM MyDB.dbo.MyTable");
        bSql.AppendLine("WHERE LEN(IdString) > 0");                 
        try
        {
            string connStr = ConfigurationManager.ConnectionStrings[conStr].ConnectionString;
            using (SqlConnection conn = new SqlConnection(connStr))
            {
                conn.Open();
                using (SqlDataAdapter adpt = new SqlDataAdapter(bSql.ToString(), conn))
                {
                    adpt.Fill(tb);
                }

            }
            return tb;
        }          
        catch (SqlException sx)
        {
            throw sx;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

此方法是同步执行的,并且 运行 经过数月的测试,在多个测试环境中都取得了成功——无论是从命令行启动还是在 AutoSys 作业的控制下启动。

然而,在投入生产时,该过程挂起了——据我们所知几乎是在 Fill 方法处。更糟糕的是,它并没有超时,而是开始生成新的请求线程,几个小时后,应用程序服务器上的内存消耗超过 5 GB。这影响了其他活跃的应用程序,使我非常不受欢迎。没有抛出异常。

连接字符串与它们的出现一样简单。

"data source=SERVER\INSTANCE;initial catalog=MyDB;integrated security=True;"

如果我对 SQL DBA 在下面报告的内容使用了错误的术语,我深表歉意,但是当我们对 SQL 服务器进行跟踪时,它显示了应用程序 ID(AutoSys 在其下job 运行ning) 被接受为有效登录。然后服务器似乎在处理 SELECT 查询。但是,它从未返回响应。相反,它进入了 "awaiting command" 状态。请求线程似乎保持打开状态几分钟,然后消失了。

DBA说没有死锁的迹象,但需要实时监控,判断是否有阻塞。

这只发生在生产环境中;在测试环境中,SQL 服务器总是在一秒钟内响应。

AutoSys 应用程序 ID 不是新的 -- 它已经与其他 SQL 服务器一起使用了好几年,没有出现任何问题。 DBA 甚至 运行 在生产 SQL 服务器上手动 运行 查询以该 ID 登录,并且响应正常。

我们一直无法在任何非生产环境中重现该问题,并且在没有服务器管理员随时待命终止进程的情况下犹豫 运行 在生产环境中解决该问题。我们的安全要求限制了我查看服务器日志和进程的权限,而且我通常必须聘请另一位专家来帮我查看它们。

我们迟早要解决这个问题。我们正在查看的数据量目前只有几行,但在接下来的几个月中会增加。根据发生的情况,我最好的猜测是它涉及应用程序服务器和 SQL 服务器之间的通信 and/or 安全。

欢迎提出任何其他想法或要调查的项目。谢谢大家。

这可能与权限有关。 SQL 服务器有时会做一些奇怪的事情而不是给出正确的错误消息。

我的建议是在执行 select 的服务器端编写一个存储过程并调用该存储过程,这无论如何都可能提高性能。这样,DBA 可以确保您可以正确访问存储过程,而不允许直接访问 table(如果由于某种原因被阻止),而且您应该会看到轻微的性能提升。

虽然它可能是由 提到的一些奇怪的 permissions/ADO.NET 问题引起的,但我还是建议您再检查一次:

  1. 确保由 DBA 手动查询 运行 并在您的应用程序中执行的查询是相同的 - 对其进行硬编码或至少在 运行 之前记录。安全总比后悔好。
  2. 尝试只 select 几行 - 如果可以避免,最好不要 select 整个 table,在我们的例子中 SELECT TOP 1(或 100)查询可能不会出现此类问题。也许数据比您想象的要多得多,ADO.Net 只是尽职尽责地尝试加载所有这些行。或许不是。
  3. 尝试 SqlDataReader to be sure that SqlDataAdapter does not cause any issues - yes, it uses the same DataAdapter internally,但我们至少会从可疑列表中排除那些额外的操作。
  4. 尝试用这 5 GB 的内存处理转储 - analyzing memory dumps 不是一项微不足道的任务,但理解是什么占用了这些大内存块并不难。因为我有点怀疑 ADO.NET 会无缘无故地产生很多额外的对象。