对于我支持的成熟 .Net 项目,我们目前正在探索从项目中删除 Entity Framework Core 的选项,同时仍保留我们依赖的一些 EF 功能。
在此列表的顶部,我们能够准确跟踪由于外部系统向我们发送的更新请求而发生了哪些值的变化 - 我们无法控制该系统,并且接收更新对我们来说并不罕见多次包含相同数据的请求。
目前,我们使用 EF 的更改跟踪器来查看我们正在处理的更新是否确实对一组特定列进行了任何更改,以便我们知道是否向用户通知这些更改。
在研究如何在没有 EF 及其开销的情况下实现这一目标时,我想到了 SQL Server 的OUTPUT Clause,理论上它允许我们对表执行更新,并返回我们所使用的键列的先前状态和更新状态的视图。用于通知触发器。
然而,到目前为止一切顺利,该文档的并行部分下有一条警告,指出“OUTPUT
将结果返回给客户端或表变量的[an]子句将始终使用串行计划。”。恐怕我目前对 SQL 的了解还不够强,无法判断这是否可能是一个问题。
从性能或可靠性的角度来看,这应该让我担心吗?我们执行的每次更新都以一组列为键,这些列形成(几乎在每种情况下)复合唯一键,因此即使更新的查询部分连续运行,也会产生明显的影响吗?
以以下架构为例:
CREATE TABLE user_profile
(
[id] INT IDENTITY (1,1) NOT NULL PRIMARY KEY
[username] NVARCHAR(100) NOT NULL,
[tenancy] NVARCHAR(20) NOT NULL,
[city] NVARCHAR(50) NULL,
[state] NVARCHAR(50) NULL,
[first_name] NVARCHAR(50) NULL,
[last_name] NVARCHAR(50) NULL,
[external_system_access_key] NVARCHAR(200) NULL,
CONSTRAINT [UX_user] UNIQUE ([username], [tenancy])
)
在此示例中,用户管理自己的city
、state
和值,但外部系统通过对我们的服务的请求进行管理first_name
,例如last_name
external_system_access_key
POST /{tenancyId}/user/{username}/profile/external
{
"accessKey": "1224567890"
}
如果我们多次收到相同的更新而没有accessKey
更改值,我们想知道更新执行前后的值是否不同,以便我们知道是否通知用户密钥已更改。每个请求都会导致如下更新:
DECLARE @accessKey NVARCHAR(200) = '1234567890';
DECLARE @username NVARCHAR(100) = 'username';
DECLARE @tenancy NVARCHAR(20) = 'tenancy';
UPDATE [user_profile]
SET [remote_system_access_key] = @accessKey
OUTPUT INSERTED.id, DELETED.[remote_system_access_key] AS OLD_remote_system_access_key, INSERTED.[remote_system_access_key] AS NEW_remote_system_access_key
WHERE
[username] = @username AND [tenancy] = @tenancy;
如果请求为我们提供了该列的新值,则每个 和OLD_
输出NEW_
列都会有不同的值,如果这是我们之前收到的请求,那么它们将匹配,从而允许我们评估之后的任何更改插入完成。
但 SQL Server 的文档说这总是会导致串行执行计划。我需要知道的是:这是一个问题吗?如果我能得到任何帮助来理解这一点及其潜在影响,我将不胜感激。
通常,每次更新一次只会命中一行,因为其 where 子句使用复合唯一键。我们只是想避免 EF 所做的事情,需要先查询数据 - 相反,由于 SQL Server 已经提供了一种机制来了解更新前后的状态,因此我们希望事后取回该数据如果它不会导致性能问题。
与我们的实际情况相比,我上面使用的示例非常简单,我们将在每个语句中更新大量列 - 全部在一个表中,但每个表中有多个列。尝试涵盖每个请求中可能更新的内容的所有可能排列将是极其复杂的。
在这种情况下几乎可以肯定不是。
据我所知(从之前的 EF 问题)EF 并不羞于将子句添加
OUTPUT
到查询中,因此如果您跟踪查询,您很可能会发现它已经被执行了。与此无关,可以通过查找找到行的单例更新
UX_user
不太可能从并行性中受益,或者计划成本接近并行性成本阈值(其中将考虑并行计划)。也许可以通过反常地提供外键来验证而没有有用的索引(即另一个表引用您正在更新的列)来设计这种情况,但您可以查看您的执行计划并看看这是否是这样的。
如果您想在计划有可能并行的地方应用此方法,那么无论如何解决方法都非常简单。您可以创建一个
#temp
表,OUTPUT INTO
然后SELECT
从#temp
表中创建。