我有一个 .net Framework 4.7.2 应用程序和 Entity Framework 6.1.2。该应用程序使用一个 azure sql 数据库,其中包含一个包含加密数据的表。 我们已经使用了 Always Encrypt sql server 的功能是为了加密这些数据。数据库架构如下所示。
Order 表中的“Description”字段为 nvarchar(100),Note 中的“Description”字段为 nvarchar(100),并使用 Always Encrypt(确定性加密)进行加密。两列都可以为空,并且都有非聚集索引。两个表都有几千万条记录。
我们的代码中有以下查询:var user = DbContext.Set<User>().FirstOrDefault(u => u.Notes.Any(n => n.Description == value));
此查询以前以正常性能成本(几十毫秒)运行
加密我们数据库中的字段。加密后这完全改变了。执行时间变为几十秒。
我重建了所有索引,但它没有改变任何东西。上面的代码语句从 Entity Framework 翻译成这样:
DECLARE @p__linq__0 nvarchar(100) = 'some text'
SELECT *
FROM [User]
WHERE EXISTS (SELECT 1 AS [C1]
FROM [Note]
WHERE ([User].[Id] = [Note].[UserId]) AND (([Note].[Description] = @p__linq__0) OR (([Note].[Description] IS NULL) AND (@p__linq__0 IS NULL))) )
下面包含其在数据库中的执行计划。从这个计划可以明显看出,描述中的索引没有被使用,而是执行了聚集索引扫描。这就是查询性能较差的原因。
棘手的部分是,如果我们删除 OR (([Note].[Description] IS NULL) AND (@p__linq__0 IS NULL)))
作为 where 子句的一部分,查询的执行立即发生,并且执行计划是预期的(见下文)
DECLARE @p__linq__0 nvarchar(100) = 'some text'
SELECT *
FROM [User]
WHERE EXISTS (SELECT 1 AS [C1]
FROM [Note]
WHERE ([User].[Id] = [Note].[UserId]) AND ([Note].[Description] = @p__linq__0) )
最有趣的是,如果我们删除 ([Note].[Description] IS NULL)
!!! where 子句的一部分在其较差的状态下性能再次下降,并且查询的执行计划是前一个。
DECLARE @p__linq__0 nvarchar(100) = 'some text'
SELECT *
FROM [User]
WHERE EXISTS (SELECT 1 AS [C1]
FROM [Note]
WHERE ([User].[Id] = [Note].[UserId]) AND (([Note].[Description] = @p__linq__0) OR ((@p__linq__0 IS NULL))) )
如果我们对 Order 表执行相同的查询,该表具有与 Note 完全相同的架构,但其 Description 字段未加密,则在所有情况下性能和执行计划都符合预期。
我已经看到以下相关问题,但它们不涉及加密列。它们指的是默认 Entity Framework 行为。 1 2
因此,问题是:在上述情况下数据加密(始终加密)会产生什么影响以及我们如何克服它?
最佳答案
这就是 EF 在 SQL 中复制 C# 空比较语义的方式。这是可选的,我总是将其关闭。
Gets or sets a value indicating whether database null semantics are exhibited when comparing two operands, both of which are potentially nullable. The default value is false.
For example (operand1 == operand2) will be translated as:
(operand1 = operand2)
if UseDatabaseNullSemantics is true, respectively
(((operand1 = operand2) AND (NOT (operand1 IS NULL OR operand2 IS NULL))) OR ((operand1 IS NULL) AND (operand2 IS NULL)))
DbContextConfiguration.UseDatabaseNullSemantics
在 DbContext 构造函数中只需设置:
this.Configuration.UseDatabaseNullSemantics = true;
在 EF Core 中,配置是在 OptionsBuilder 上完成的,例如
optionsBuilder.UseSqlServer(constr, o => o.UseRelationalNulls());
关于.net - 加密列后 SQL 查询性能下降,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64500969/