【问题标题】:Stored procedure and trigger execution blocking存储过程和触发器执行阻塞
【发布时间】:2020-02-13 15:27:32
【问题描述】:

我有 SQL Server 2017 Express 数据库,最多可供 6 台平板电脑访问,这些平板电脑使用 REST Web 服务通过 Angular 7 应用程序连接。

我有一个允许将新用户插入特定数据库表的存储过程。插入总是一次只插入 1 条记录,但是对于 6 个客户端,每个客户端几乎可以同时调用存储过程。

该过程的最后一步是将协议表单打印到特定打印机。最初这将在客户端处理,但平板电脑无法打印到网络打印机,因此该功能现在需要驻留在服务器端。

根据这一新要求,协议表单是一个正在读取的 RTF 文档,占位符值替换为插入语句中的值,写入临时文件,然后通过应用程序打印到网络打印机(很可能是写字板)与 RTF 文件格式相关联。

还有一个 MS Access 前端应用程序,它使用链接服务器连接到数据库,但没有创建新用户的能力,但需要能够启动“打印协议”操作因打印机问题、网络问题等导致协议无法打印的情况。

我编写了 C# 代码来执行表单的读取/修改/写入/打印操作,该表单使用 UseShellExecute StartInfo 属性和 Process.Start 方法。

由于文件读取/修改/写入/打印过程需要几秒钟,我担心存储过程会在这段时间内添加注册阻塞。

我很确定我需要一个 CLR 存储过程,以便 MS Access 前端可以启动打印操作,所以我想出的是“Add_Registration”存储过程(Transact-SQL ) 将调用 CLR 存储过程来执行读取/修改/写入/打印操作,或调用 CLR 存储过程读取/修改/写入/打印的表上的插入触发器(CLR 或 Transact-SQL)。

如果有令人信服的理由,我可以通过在 CLR 触发器和 CLR 存储过程中复制代码来避免从触发器调用存储过程,但如果可能的话,我会尽量避免重复代码。

我目前考虑的解决方案如下,但不确定SQL Server如何处理各种场景:

  1. 注册表上的 CLR 或 Transact-SQL Insert 触发器调用执行读取/修改/写入/打印过程的 CLR 存储过程。

  2. 执行读取/修改/写入/打印过程的 CLR 存储过程,从当前的add_registration Transact-SQL 存储过程调用

我不断重复的问题是:

  1. InsertCLR 触发器是如何执行的,如果多个插入同时或几乎同时完成(每个操作只有 1 个),它们是排队然后同步处理还是立即执行?

  2. 与 #1 相同的问题,但使用 Transact-SQL 触发器

  3. 如果 CLR 存储过程被多个客户端同时或几乎同时调用,它们是排队然后同步处理,还是立即执行对存储过程的每个调用,如何处理它们?

  4. 除了 Transact-SQL 存储过程之外,问题与 #3 相同

  5. 如果从 Transact-SQL 触发器调用 CLR 存储过程,触发器是在存储过程返回之前被阻塞,还是对存储过程的调用通过触发立即返回?

  6. 与 #5 相同的问题,但 CLR 触发器调用 CLR 存储过程

我正在寻找有关 SQL Server 如何处理这些场景的任何其他建议和/或说明。

【问题讨论】:

  • 对我来说,这听起来更像是一份报告,而不是一堆 CLR。您是否为此考虑过 SSRS 报告?这将比使用 CLR 的过程和触发器简单一百万倍。
  • 我应该更清楚地了解这些协议。我查看了报告服务,但是非常缺乏经验的用户需要使用他们拥有和知道的工具修改协议,因此客户决定他们可以使用 Word 做到“最好”(我至少让他们同意 RTF 格式,因为它更容易编辑)。
  • 你知道SSRS可以直接导出到Word吗?每个报告顶部的工具栏右侧都有一个小圆盘图标。您可以在当前查看器或 Excel、pdf 或 Word 中打开报告。对于没有经验的用户来说,再简单不过了。 :D
  • @SeanLange 没有导出到 Word 是问题所在,输出文件在打印后未保存。问题在于他们能够使用他们拥有和知道的工具来修改文档模板,而不必为了修改文档模板而学习新东西。我读到的有关 Reporting Services 的一点信息表明您必须使用与 SSDT 一起安装的 Report Builder 或 Report Designer。
  • 是的,要开发报表,您必须使用报表设计器。但最终用户甚至不知道该工具的存在。他们只需单击应用程序中的某些内容,即可在特定 url 处打开网页(使用查询字符串值向报告提供一些信息)。用户只是在 Web 浏览器中看到报告。他们没有能力修改报告的模板。但听起来在你的情况下他们无论如何都没有,所以这没什么大不了的。

标签: sql-server stored-procedures clr database-trigger sqlclr


【解决方案1】:

除非您自己实现排队,否则没有排队,并且有几种方法可以做到这一点。所以对于多个并发会话,它们都是独立的。当然,在写入数据库(INSERT/UPDATE/DELETE/等)时,它们显然按照提交请求的顺序操作。

我不确定您为什么要在其中包含触发器,但由于它与并发性有关,触发器在系统生成的事务中执行,该事务由触发触发器的 DML 语句启动。这并不意味着您将拥有单线程 INSERT,但如果触发器正在调用需要一两秒才能完成的 SQLCLR 存储过程(并且触发器在存储过程完成/退出之前无法继续),那么就会有锁在该操作期间被搁置在桌面上。这些锁可能不会阻止其他会话同时插入,但您可能有其他操作试图修改需要冲突锁的表,该锁需要等到插入 + 触发器 + SQLCLR proc 操作完成。当然,这可能只有在您频繁插入时才会成为问题,但这取决于您期望新用户的频率,而这可能不足以让您担心。


我也不确定为什么你需要在那一刻打印任何东西。如果您只是有一个标志/BIT 列指示是否已打印协议,则在几个级别上可能会简单得多,默认为0。然后,您可以拥有一个通过 SQL Server 代理或 Windows 计划任务计划的外部控制台应用程序,每隔几分钟执行一次,该应用程序从 UsersWHERE HasPrintedAgreement = 0 中读取。每行都有替换值的必要字段,它打印每个字段,并在打印时更新UserID 设置HasPrintedAgreement = 1。如果您总是希望协议立即生效,您甚至可以安排此控制台应用每分钟执行一次。

【讨论】:

  • 立即打印的原因是他们必须对文档进行物理签名并验证数据,然后才能将注册移动到活动表中。注册以“团体”模式进行,其中 3-4 人同时进入并注册,然后您可以在下一个进入的团体需要注册之前进行一个小时或更长时间。他们只需在第一次访问该设施时进行登记。
  • “IsPrinted”是我正在研究的其他解决方案之一(Windows 服务或“自动运行”应用程序,但认为将它放在数据库中会使事情更加集中,移动更少如果最初的应用程序没有在 Access 中完成(最初的应用程序只是有人试图跟踪纸质协议 - 然后他们得到了平板电脑,所以他们当然需要自动化注册过程),我会产生一个应用程序中的线程会定期检查新记录。
  • @EricB 正如我所说,您可以在 SQL Server 代理中安排它。这使它在 SQL Server 中更加集中,而不是在进程中。如果需要,您甚至可以安排它每秒运行一次,在这种情况下,它一次不会运行多个进程。这与以相同频率进行服务检查实际上不是一回事吗?我认为这并不比通过触发器的“实时”打印慢得多,但与使用几乎不会立即完成的触发器相比,它是一种更可靠的解决方案,还可以减少争用。
  • 我以某种方式阅读了“SQL Server 代理”,只看到了“Windows 计划任务”。我将在早上研究 SQL Server 代理的第一件事。谢谢你的想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-25
  • 1970-01-01
  • 1970-01-01
  • 2013-12-30
  • 1970-01-01
  • 2021-07-18
  • 1970-01-01
相关资源
最近更新 更多