SQL Server CLR Threading
本文关键字:Threading CLR Server SQL | 更新日期: 2023-09-27 18:32:42
我一直在为SQL Server CLR存储过程而苦苦挣扎。
背景:
我们使用的是 SQL Server 2014,并且已实现调用客户的 Web 服务的 CLR 存储过程。
线程最初用于不减慢 SQL Server CLR 的主线程。
虽然,现在,我知道在CLR下使用线程不是最好的主意,但它已经正常工作了6年(自SQL Server 2008以来(。它最近已迁移到SQL Server 2014。
问题所在
在我的开发机器上,与在测试系统上一样,我们的解决方案没有问题。
在客户系统上,由于某种原因,调用 Web 服务的线程永远不会执行。
我可以从日志文件中看到,在线程执行之前一切正常。
没有特定的错误,什么都没有。
我们一直在尝试更改权限,但没有成功。因此,我认为这不是权限问题。
问题
-
有谁知道如何改变行为?我们找不到任何可能解决问题的配置。
-
完全删除线程并直接在 SQL Server 主线程上调用 Web 服务是个好主意吗?
谢谢你的任何建议,切赫
不确定问题 #1,尽管考虑到问题 #2 的建议可能无关紧要。尽管如此,SQL Server 2008(它正在工作的地方(和SQL Server 2014(它不工作的地方(之间的一个区别是SQL Server链接到的CLR版本。SQL Server 2005/2008/2008 R2链接到CLR v2.0,而SQL Server 2012及更高版本链接到CLR v 4.0。由于您没有看到错误,而您的客户端是,我将确保他们的系统已更新到您正在运行的相同.NET Framework版本。
对于问题#2,我建议删除多线程。这有太多可能出现问题,需要大会UNSAFE
。如果删除螺纹,则可以将程序集设置为 EXTERNAL_ACCESS
。
如果要减少争用,则假定 Web 服务调用是针对同一 URI,则需要增加允许的并发 Web 请求数。这可以通过设置 ServicePointManager.DefaultConnectionLimit 属性来完成。默认值为 2。这意味着,任何其他请求都将等待并等待,直到当前 2 个请求中的一个关闭。
另外,请务必正确Dispose
WebRequest
。
对于进行可能无法快速完成的外部调用(即 Web 服务(的担忧是,SQL Server 使用协作多任务处理,其中每个线程负责在不同点将控制权"交出"回调度程序(有效地暂停它(,以便调度程序可以打乱事情并运行当前"休眠"的其他内容。通常可以通过执行以下至少一项操作来缓解有关 SQLCLR 代码的这种担忧:
- 执行数据访问/查询实例
- 呼叫
thread.sleep(0);
但是,外部调用不执行数据访问,并且在等待WebResponse
完成时无法轻松调用thread.sleep(0)
。是的,您可以在单独的线程上调用 WebService,并在等待它完成时,假设您只是循环和检查,sleep(x)
将允许产量。
但是,异步执行 Web 服务调用是否必要?它当然有要求将大会标记为WITH PERMISSION_SET = UNSAFE
的缺点。这在很大程度上取决于呼叫通常需要多长时间以及呼叫频率。调用越频繁,任何延迟(至少部分(由每个 URI 允许的并发连接数的默认值较低引起的可能性就越大。这与我在顶部提出的建议有关。
但是,如果你想看看SQL Server的实际工作原理,这应该很容易测试。在我的笔记本电脑上,我转到对象资源管理器中的服务器"属性",转到"处理器",取消选中"自动设置处理器关联..."选项,在对话框中间的树视图中的"处理器关联"下仅选择单个 CPU,单击"确定",然后重新启动服务。然后,我设置了一个网页,除了调用"睡眠"60秒之外什么都不做。我有一个调用网页的 SQLCLR TVF,所以我在两个不同的选项卡/会话中同时运行它。在第三个选项卡/会话中,我运行了:
SELECT SUM(so1.[schema_id]), so1.[type_desc], so2.[type_desc]
FROM sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3
CROSS JOIN sys.objects so4
CROSS JOIN sys.objects so5
WHERE so3.[create_date] <> so4.[modify_date]
GROUP BY so1.[type_desc], so2.[type_desc], so5.[name]
ORDER BY so2.[type_desc], so5.[name] DESC;
最后,在第 4 个选项卡中,在启动前 3 个选项卡后,我运行以下命令来监控系统:
SELECT * FROM sys.dm_os_schedulers WHERE [scheduler_id] = 0;
SELECT *
FROM sys.dm_exec_requests
WHERE [scheduler_id] = 0
AND [status] <> N'background'
ORDER BY [status] DESC, session_id;
运行 SQLCLR 函数的 2 个会话的状态始终为"正在运行",而运行选项卡 3 中该丑陋查询的会话的状态始终为"可运行"。但可以肯定的是,当两个 SQLCLR 函数都没有执行时,再次运行这个丑陋的查询所花费的时间与与运行 SQLCLR 调用的 2 个会话同时运行时花费的 1 分 14 秒相同,该会话对休眠的网页进行了 60 秒。
请不要推断运行 SQLCLR 代码进行 Web 调用没有成本。由于这些线程一直处于繁忙状态,因此如果系统繁忙,则会降低 SQL Server 分配这些线程以更快地完成其他查询的能力。但似乎可以肯定的是,至少在负载低到中等的系统上,通过添加线程获得的好处似乎不值得增加复杂性的成本(特别是因为现在有一个尚未重现的问题需要调试(。