.net - 加密列后 SQL 查询性能下降

标签 .net sql-server entity-framework always-encrypted

我有一个 .net Framework 4.7.2 应用程序和 Entity Framework 6.1.2。该应用程序使用一个 azure sql 数据库,其中包含一个包含加密数据的表。 我们已经使用了 Always Encrypt sql server 的功能是为了加密这些数据。数据库架构如下所示。

enter image description here

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))) )

下面包含其在数据库中的执行计划。从这个计划可以明显看出,描述中的索引没有被使用,而是执行了聚集索引扫描。这就是查询性能较差的原因。

enter image description here

棘手的部分是,如果我们删除 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) )

enter image description here

最有趣的是,如果我们删除 ([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/

相关文章:

c# - 使 Math.Log 在 Entity Framework 查询中工作

c# - 为什么 IList<T> 不提供 List<T> 提供的所有方法?我应该使用哪个?

SQL Server 和类型的隐式转换

sql-server - SQL Server : Insert Result of WHOAMI into Table

c# - 如何使用 EntityFramework 构建富领域模型

asp.net - 如何 "warm-up" Entity Framework ?什么时候得到 "cold"?

c# - 在 .NET 中动态添加引用

.net - .NET 的 SabreDAV?

java - 列索引无效

c# - 遍历 DbCommand 中的参数