微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

加密列后,SQL查询性能下降

如何解决加密列后,SQL查询性能下降

我有一个带有实体框架6.1.2的.net框架4.7.2应用程序。该应用程序使用Azure sql数据库,该数据库除其他外还具有带加密数据的表。 我们已经使用sql server的Always Encrypt功能来加密这些数据。数据库架构如下所示。

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));查询以前以正常的性能成本(几十毫秒)运行 加密数据库中的字段。加密后,这种情况完全改变了。执行时间更改为几十秒。

我重建了所有索引,但没有任何改变。上面的代码语句从实体框架转换为类似的东西

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

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

enter image description here

棘手的部分是,如果我们删除where子句的OR (([Note].[Description] IS NULL) AND (@p__linq__0 IS NULL)))部分,查询的执行将立即发生,并且执行计划是预期的执行计划(见下文)

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

如果我们对具有与Note完全相同的架构的Order表执行相同的精确查询,但其Description字段未加密,则在所有情况下性能和执行计划都是预期的。

我已经看到以下相关问题,但它们未涉及加密列。它们指的是认实体框架行为。 1 2

因此,问题是:在上述情况下,数据加密(始终加密)会产生什么影响,我们将如何克服?

解决方法

这是EF在SQL中复制C#空比较语义的方式。这是可选的,我总是将其关闭。

for X in range(1,397):
    dirname = os.path.join('.','Beta' + str(X),'')
    output  = os.path.join('.','output.pdf')

DbContextConfiguration.UseDatabaseNullSemantics

只需在您的DbContext构造函数中设置:

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

在EF Core中,配置是在OptionsBuilder上完成的,例如

this.Configuration.UseDatabaseNullSemantics = true;
,

最后,该问题与数据加密无关。 实际的问题是由于大数据迁移后数据库的统计信息已过时。我们使用EXEC sp_updatestats更新了它们,然后使用预期的执行计划和预期的性能执行了查询。

,

我怀疑这与加密无关,并且与这个catch-all子句无关:

AND (([Note].[Description] = @p__linq__0) OR ((@p__linq__0 IS NULL)))

此类包罗万象的子句是尝试在存储过程中使用“可选”参数的常见但不幸的方式。不幸的是,他们cause performance issues就是这样。服务器在第一次运行查询时缓存执行计划,并在后续调用中重用它。 不需要需要查询的特定列的执行计划与执行该查询的计划有很大不同。

如果第一次调用使用null作为参数,则生成的执行计划将不使用覆盖该列的任何索引-为什么要这么做?

在使用诸如EF之类的ORM时,尤其是在涉及LINQ时,不需要这些技巧。

我怀疑EF查询(未发布)包含这样的Where调用:

.Where(note=> note.Description == text || text ==null);

您可以链接LINQ调用,这意味着您只能在需要时添加条件。 catch-all调用可以替换为:

if (text != null)
{
    query=query.Where(note => note.Description == text);
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?