【问题标题】:Identifying a specific thread's entry in dm_exec_requests在 dm_exec_requests 中识别特定线程的条目
【发布时间】:2015-03-24 22:54:13
【问题描述】:

我有一组正在执行的工作进程 (System.Threading.Thread) MSSQL Server 上的查询。在某些情况下,(即长期修复的错误)查询需要很长时间才能执行,并且我被要求为管理员用户(此应用程序的,而不是数据库管理员)提供一种方法从应用程序中找到并终止该查询

我在我的 DBMS 中找到了this post with a SQL query I can use to get a list of open SQL commands,但我似乎无法在结果集中找到任何唯一标识一个特定线程挂起的 SqlCommand/Transaction/Connection 的数据

背景信息:为了使用 Visual Studio 2013 和 SSMS 研究这种可能的解决方案,我创建了一个类(在 VS,.net 4.5 中),它包含一对线程,“worker”和“watcher”。 Worker 打开一个事务,并在其包装类上为其分配一个引用(因此我可以从“观察者”线程的角度在断点处进行一些探索)。然后,它运行一个“模拟长时间运行的查询”命令,一个 WAITFOR... 同时,Watcher 休眠,直到工作人员等待长时间运行的查询完成,然后命中一个断点。此时,我切换到 SSMS 并从上面的链接运行查询的修改版本:

SELECT * FROM sys.dm_exec_requests req CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext WHERE wait_type = 'WAITFOR'

然后我返回 VS 并深入研究本地变量(包括对“Worker”创建的 SqlTransaction、SqlConnection 和 SqlCommand 的实例本地引用),以尝试找到任何字段的值与SELECT 查询的结果集...

下面是运行这些查询的类类型示例。 (忽略任何关于连接/事务等的挑剔细节 - 请放心,我的连接已打开,所有基本的东西都在起作用)

public class WorkerThreadTest {
    private SqlTransaction workerTransaction;
    private SqlConnection workerConnection;
    private SqlCommand workerCommand;

    private System.Threading.Thread worker;
    private System.Threading.Thread watcher;

    WorkerThreadTest() {
        worker = new System.Threading.Thread(new System.Threading.ThreadStart(Run));
        watcher = new System.Threading.Thread(new System.Threading.ThreadStart(Watch));
        worker.Start();
        watcher.Start();
    }

    private void Run() {
        // Paraphrasing here //
        workerTransaction = new Transaction(foo); 
        workerConnection = new SqlConnection(foo);

        // Again, paraphrasing:
        workerCommand = new SqlCommand("WAITFOR DELAY '0:05';", workerConnection, workerTransaction);
        workerCommand.Execute(foo); // Thread will wait on this line for 5 minutes
        bool placeBreakPointHere = true;
    }

    private void Watch() {
        System.Threading.Thread.Sleep(foo); // Wait enough time for worker to execute its command
        bool placeBreakPointHere = true;
        // Here is where I explore my locals
    }
}


// Inside main program:
WorkerThreadTest myWorkerThread = new WorkerThreadTest();

这是在工作线程执行 WAITFOR 后从 SQL 查询返回的示例行:

session_id  request_id  start_time  status  command sql_handle  statement_start_offset  statement_end_offset    plan_handle database_id user_id connection_id   blocking_session_id wait_type   wait_time   last_wait_type  wait_resource   open_transaction_count  open_resultset_count    transaction_id  context_info    percent_complete    estimated_completion_time   cpu_time    total_elapsed_time  scheduler_id    task_address    reads   writes  logical_reads   text_size   language    date_format date_first  quoted_identifier   arithabort  ansi_null_dflt_on   ansi_defaults   ansi_warnings   ansi_padding    ansi_nulls  concat_null_yields_null transaction_isolation_level lock_timeout    deadlock_priority   row_count   prev_error  nest_level  granted_query_memory    executing_managed_code  group_id    query_hash  query_plan_hash statement_sql_handle    statement_context_id    dbid    objectid    number  encrypted   text
112 2   2015-03-24 16:34:12.433 suspended   WAITFOR 0x01004300C7A0DC2CB05D23780200000000000000000000000000000000000000000000000000000000000000  118 -1  0x06004300C7A0DC2CF07EA34A0400000001000000000000000000000000000000000000000000000000000000  67  1   9E1EF06D-A755-4EBD-82AA-B030F8B6D19B    0   WAITFOR 72294   WAITFOR     1   1   433172712   0x0000FFFFFFFFFFFFFFFF  0   0   0   72294   4   0x0000000243685468  0   0   0   -1  us_english  mdy 7   1   0   1   0   1   1   1   1   2   -1  0   0   0   2   0   0   2   NULL    NULL    NULL    NULL    67  NULL    NULL    0   (@ContextInfo varbinary(10))SET CONTEXT_INFO @ContextInfo WAITFOR DELAY '0:05';

有什么方法可以唯一标识与我的工作线程的特定实例匹配的行?

(注意:不能保证主机名、ip 或查询文本是唯一的)

【问题讨论】:

  • 另外,我不偏爱 dm_exec_requests,任何识别 session_id 的方法,或任何其他杀死查询 DBMS 端的方法都可以。

标签: c# .net sql-server multithreading


【解决方案1】:

我会做这样的事情。

在应用程序中,我将显示所有查询的列表,类似于 SSMS。如您所知,该列表可以从sys.dm_exec_requests 获得。实际上,我不会显示所有查询,而是那些运行时间超过某个阈值的查询(使用start_time),即那些可能应该停止的查询。

应用程序将通过其会话 ID 向kill 发送任何查询的命令。

我认为不需要在应用程序的其余部分中找到运行应该被终止的查询的特定线程。一旦查询被终止,线程的Execute 很可能会抛出一些异常。捕获并相应地处理它。长时间运行的查询可能已由另一台客户端计算机上的另一个应用程序实例启动,因此有关客户端线程的任何信息都是无用的。

因此,最终用户会看到服务器上已运行很长时间的所有查询的列表,可以查看到目前为止它们已经运行了多长时间,并且可以终止任何此类查询。

为什么这种方法不适合你?

【讨论】:

  • 感谢您的建议 - 此解决方案不起作用的原因是我们无法对“太长”进行定义 - 在某些情况下,报告可能是预期的 i> 需要很长时间(我们将处理“大数据”以及年终流程)。因此,必须将终止报告的决定留给用户。此外,还有多个安全配置文件在起作用,因此一个用户可能无法看到另一个用户或组运行的查询。因此,我们需要向用户显示一个列表,其中仅包含他/她可以修改的线程,然后允许他/她自行决定取消一个。
  • 所以,这意味着您需要以某种方式过滤sys.dm_exec_requests 的输出。不过,您不需要识别特定的线程。你只需要弄清楚用户有权杀死哪些查询。您可以从查看HostNameLogin 开始,并仅向用户显示源自其计算机并在其登录下的那些查询。尽管如此,主要思想是您终止查询,而不是线程,当查询被终止时,线程将自行终止。
  • 另外,您已经知道可以使用sys.dm_exec_sql_text 检索正在运行的查询的文本,因此您可以在查询文本中包含一些内容来帮助您确定您需要什么,例如报告名称或线程 ID,或用户 ID。以不影响查询本身的方式。我会尝试将此信息添加到查询文本的注释中,或额外的PRINT 语句或存储过程的额外参数中,并检查它是否通过sys.dm_exec_sql_text 可见。
  • 是的,最后一个是我决定采取的路线 - 我在线程内的评论中对唯一标识信息进行编码,并在 LIKE %id% 过滤器中使用该唯一 ID 来隔离查询问题。谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-03-22
  • 1970-01-01
  • 1970-01-01
  • 2021-10-09
  • 2018-08-07
  • 2018-11-16
  • 2017-03-16
  • 1970-01-01
相关资源
最近更新 更多