SQL Server Service Broker的陷阱

目录

介绍

背景

SQLDependency 要求

案例介绍

什么是构建

进一步调查

提醒

看法


介绍

C# SQLDependency对象中使用查询并将该查询设置为数据库中的事件时,很容易误用SQL Server中的服务代理。当查询从多个表请求数据时,数据库最终可能会陷入数据锁定状态。当您遇到此问题时,您将从BINGGoogle获得0个答案来解决这个问题。

在微软官方文档中,为查询设置了要求。只要保持查询简单,就很容易在数据库中造成灾难而不知情。特别是对于懒惰的开发人员来说,这是一个学习曲线。此外,对于微软来说,它对我们的情况做出了快速反应,但在两周后似乎失去了兴趣,因为我表示我的发现并非不可想象。如何与服务代理合作而不会遇到麻烦是一个感知问题。

还有更多关于服务代理和问题的文章,但没有一篇指出原因:

很好的例子:

在本文中,给出了一个修复程序。

背景

在我们的生产环境中,我们有时会出现很大的性能下降。去年,它已成为日常问题。简单的更新查询最多需要2分钟。该更新的查询计划显示了一个充满并行性的巨大计划。查询如下所示:

UPDATE table SET FIELD = VALUE Where ID = IdValue;

因此,并行性是完全出乎意料的行为。重新启动数据库后,正常的查询计划再次出现。一天之内,问题又回来了。

SQLDependency 要求

Microsoft在此处提供了你在SQLDependency中使用的查询的下一个要求。

在支持的SELECT语句的标题下,未提及最重要的规则:语句不得包含JOIN。我将解释为什么我认为您不应该在查询中使用超过1个表。

案例介绍

我们的数据库(近100个表)有6个代表工作流程的重要表格。这些表可以通过主键ID字段联接。这6个表包含47个最需要的列,以便能够了解客户项目中涉及的内容和完成的工作。测量完成后,机组人员必须拒绝或接受测量。该复选框是服务代理应响应的事件。在该事件之后,其他流程将在我们的工作流程中启动。

什么是构建

在设置SQLDependency的服务中,开发人员认为在查询中询问所有这47个字段是明智的。由于不禁止在这些查询中使用JOIN,并且查询非常简单,因此没有给出任何错误。用于测量的应用程序解决了所有问题。更好的是,我的电话响个不停。

我们最初认为测量系统有问题,因为我们在那里看到了问题。额外的问题:我们永远无法分辨6个表中的哪一个必须处理update查询中的并行性问题。它总是更新。给定时间后,我们决定停止具有依赖项的服务,每个人都可以再次工作。

那是我们调查查询的那一刻。我们真正需要从活动中了解什么?

我们的查询如下所示:

SELECT tbl1.Field1, tbl1.Field2, tbl1.Field3, _
       tbl1.Field4, tbl1.Field5, tbl1.Field6, tbl1.Field7,
tbl2.Field1, tbl2.Field2, tbl2.Field3, tbl2.Field4, _
             tbl2.Field5, tbl2.Field6, tbl2.Field7,
tbl3.Field1, tbl3.Field2, tbl3.Field3, tbl3.Field4, _
             tbl3.Field5, tbl3.Field6, tbl3.Field7,
tbl4.Field1, tbl4.Field2, tbl4.Field3, tbl4.Field4, _
             tbl4.Field5, tbl4.Field6, tbl4.Field7,
tbl5.Field1, tbl5.Field2, tbl5.Field3, tbl5.Field4, _
             tbl5.Field5, tbl5.Field6, tbl5.Field7,
tbl6.Field1, tbl6.Field2, tbl6.Field3, tbl6.Field4, _
             tbl6.Field5, tbl6.Field6, tbl6.Field7, tbl6.lastModified
FROM dbo.Table1 as tbl1
Join dbo.Table2 as tbl2 on tbl2.table1Id = tbl1.Id
Join dbo.Table3 as tbl3 on tbl3.table2Id = tbl2.Id
Join dbo.Table4 as tbl4 on tbl4.table3Id = tbl3.Id
Join dbo.Table5 as tbl5 on tbl5.table4Id = tbl4.Id
Join dbo.Table6 as tbl6 on tbl6.table5Id = tbl5.Id
Where tbl6.lastModified > @lastModified
AND tbl6.IsAccepted IS NOT NULL

根据微软的说法,这是对SqlDependency对象的有效查询。

进一步调查

上述查询的最大问题是没有人设置字段IsAccepted。当一个拥有400人的组织向所有这6个表添加数据行时,元组的集合会变得很大,因为您永远等待IsAccepted设置为falsetrue。因此,重新启动SQL服务器后,大约需要一个小时才能再次出现相同的问题。

提醒

首先,您必须知道SQLDependency在服务代理中设置为最多2分钟。如果事件发生较早,则该事件会通知应用程序它已发生。如果没有,则服务代理将再次设置事件2分钟。

服务代理将您的查询设置为数据库中的索引视图,模式系统!这是微软的某个人可以告诉我的。在这里,当连接SQLDependency中的多个表时,问题就开始了。我们使用了6个表中的47个字段,而我们只需要知道测量被接受或拒绝后测量表的涉及Id

因此,当在索引视图中创建 47 个字段(优先级较低 - 仍然是优先级)时,它会对您的数据库产生各种影响。我们在查询中发现的并行性显示了一些奇怪的信息。UPDATE

<OutputList>
    <ColumnReference Database="[TDMS-TEST]" Schema="[sys]" 
    Table="[query_notification_1626853258]" Column="id" />

您应该在数据库中将结尾的数字读取为object_id。通过下一个查询,您可以找到以下索引:

SELECT IndexName = i.Name, ColName = c.Name,  *
FROM sys.indexes i
     INNER JOIN sys.index_columns ic ON ic.object_id = i.object_id
                                        AND ic.index_id = i.index_id
     INNER JOIN sys.columns c ON c.object_id = ic.object_id
                                 AND c.column_id = ic.column_id
WHERE 1 = 1
AND I.OBJECT_ID = 1626853258
ORDER BY i.Name;
You cannot drop these indexes. The service broker can!

但是,当要求将6个表中的47个字段作为索引视图进行监视时,您的数据库需要每2分钟创建和删除索引。继续检查6个表,其中1个表是否有更改。

看法

我们真正需要从服务代理那里得到什么?在一个表中,一个位域相对于上一次修改的值从null变为非null的事件。那么为什么我们需要其他45个字段呢?只是在生产数据库中性能下降。

在我们的例子中,我们希望每个人都提供大量数据。最好将查询分为两部分。

1部分是依赖项查询,它反映了要通知的事件。第2部分是满足数据需求所需的查询。

例:

DependencyQuery就像这样:

SELECT tbl6.Id, tbl6.lastModified
FROM dbo.Table6 as tbl6
WHERE tbl6.LastModified > @LastModified
AND tbl.IsAccepted IS NOT NULL

工作流的查询为:

SELECT tbl1.Field1, tbl1.Field2, tbl1.Field3, tbl1.Field4, _
       tbl1.Field5, tbl1.Field6, tbl1.Field7,
tbl2.Field1, tbl2.Field2, tbl2.Field3, tbl2.Field4, _
             tbl2.Field5, tbl2.Field6, tbl2.Field7,
tbl3.Field1, tbl3.Field2, tbl3.Field3, tbl3.Field4, _
             tbl3.Field5, tbl3.Field6, tbl3.Field7,
tbl4.Field1, tbl4.Field2, tbl4.Field3, tbl4.Field4, _
             tbl4.Field5, tbl4.Field6, tbl4.Field7,
tbl5.Field1, tbl5.Field2, tbl5.Field3, tbl5.Field4, _
             tbl5.Field5, tbl5.Field6, tbl5.Field7,
tbl6.Field1, tbl6.Field2, tbl6.Field3, tbl6.Field4, _
             tbl6.Field5, tbl6.Field6, tbl6.Field7
FROM dbo.Table1 as tbl1
Join dbo.Table2 as tbl2 on tbl2.table1Id = tbl1.Id
Join dbo.Table3 as tbl3 on tbl3.table2Id = tbl2.Id
Join dbo.Table4 as tbl4 on tbl4.table3Id = tbl3.Id
Join dbo.Table5 as tbl5 on tbl5.table4Id = tbl4.Id
Join dbo.Table6 as tbl6 on tbl6.table5Id = tbl5.Id
Where tbl6.Id = @Id

因此,在C#类中,我们维护在服务代理中设置为事件的表的lastmodified值(以防止内存中有大量记录)。

结果(可能超过1行记录,具体取决于数据库的使用范围)后,我们将Id值设置为第二个查询的参数,该查询直接调用数据库。

https://www.codeproject.com/Articles/5344594/Pitfalls-of-the-SQL-Server-Service-Broker

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值