- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在使用 Entity Framework (代码优先)并发现我在 LINQ 查询中指定子句的顺序对性能有巨大影响,例如:
using (var db = new MyDbContext())
{
var mySize = "medium";
var myColour = "vermilion";
var list1 = db.Widgets.Where(x => x.Colour == myColour && x.Size == mySize).ToList();
var list2 = db.Widgets.Where(x => x.Size == mySize && x.Colour == myColour).ToList();
}
如果(稀有)颜色子句在(常见)尺寸子句之前,它的速度很快,但反之则慢几个数量级。该表有几百万行,有问题的两个字段是 nvarchar(50),因此没有规范化,但它们都被索引了。这些字段以代码优先方式指定,如下所示:
[StringLength(50)]
public string Colour { get; set; }
[StringLength(50)]
public string Size { get; set; }
我真的应该在 LINQ 查询中担心这些事情吗,我认为这是数据库的工作?
系统规范是:
是的,对于任何贪吃的人来说,效果都可以复制如下。这个问题似乎对许多因素非常敏感,所以请忍受其中一些人为的性质:
通过 nuget 安装 EntityFramework 6.0.0-beta1,然后生成代码优先样式:
public class Widget
{
[Key]
public int WidgetId { get; set; }
[StringLength(50)]
public string Size { get; set; }
[StringLength(50)]
public string Colour { get; set; }
}
public class MyDbContext : DbContext
{
public MyDbContext()
: base("DefaultConnection")
{
}
public DbSet<Widget> Widgets { get; set; }
}
使用以下 SQL 生成虚拟数据:
insert into gadget (Size, Colour)
select RND1 + ' is the name is this size' as Size,
RND2 + ' is the name of this colour' as Colour
from (Select top 1000000
CAST(abs(Checksum(NewId())) % 100 as varchar) As RND1,
CAST(abs(Checksum(NewId())) % 10000 as varchar) As RND2
from master..spt_values t1 cross join master..spt_values t2) t3
Color 和 Size 各加一个索引,然后查询:
string mySize = "99 is the name is this size";
string myColour = "9999 is the name of this colour";
using (var db = new WebDbContext())
{
var list1= db.Widgets.Where(x => x.Colour == myColour && x.Size == mySize).ToList();
}
using (var db = new WebDbContext())
{
var list2 = db.Widgets.Where(x => x.Size == mySize && x.Colour == myColour).ToList();
}
该问题似乎与生成的 SQL 中 NULL 比较的钝化集合有关,如下所示。
exec sp_executesql N'SELECT
[Extent1].[WidgetId] AS [WidgetId],
[Extent1].[Size] AS [Size],
[Extent1].[Colour] AS [Colour]
FROM [dbo].[Widget] AS [Extent1]
WHERE ((([Extent1].[Size] = @p__linq__0)
AND ( NOT ([Extent1].[Size] IS NULL OR @p__linq__0 IS NULL)))
OR (([Extent1].[Size] IS NULL) AND (@p__linq__0 IS NULL)))
AND ((([Extent1].[Colour] = @p__linq__1) AND ( NOT ([Extent1].[Colour] IS NULL
OR @p__linq__1 IS NULL))) OR (([Extent1].[Colour] IS NULL)
AND (@p__linq__1 IS NULL)))',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',
@p__linq__0=N'99 is the name is this size',
@p__linq__1=N'9999 is the name of this colour'
go
将 LINQ 中的相等运算符更改为 StartWith() 可以解决问题,将两个字段之一更改为在数据库中不可为空也是如此。
我绝望了!
对任何赏金猎人的一些帮助,问题可以在干净的数据库中的 SQL Server 2008 R2 Web(64 位)上重现,如下所示:
CREATE TABLE [dbo].[Widget](
[WidgetId] [int] IDENTITY(1,1) NOT NULL,
[Size] [nvarchar](50) NULL,
[Colour] [nvarchar](50) NULL,
CONSTRAINT [PK_dbo.Widget] PRIMARY KEY CLUSTERED
(
[WidgetId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX IX_Widget_Size ON dbo.Widget
(
Size
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX IX_Widget_Colour ON dbo.Widget
(
Colour
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
insert into Widget (Size, Colour)
select RND1 + ' is the name is this size' as Size,
RND2 + ' is the name of this colour' as Colour
from (Select top 1000000
CAST(abs(Checksum(NewId())) % 100 as varchar) As RND1,
CAST(abs(Checksum(NewId())) % 10000 as varchar) As RND2
from master..spt_values t1 cross join master..spt_values t2) t3
GO
然后比较以下两个查询的相对性能(您可能需要调整参数测试值以获得返回几行的查询以观察效果,即第二个查询id慢得多).
exec sp_executesql N'SELECT
[Extent1].[WidgetId] AS [WidgetId],
[Extent1].[Size] AS [Size],
[Extent1].[Colour] AS [Colour]
FROM [dbo].[Widget] AS [Extent1]
WHERE ((([Extent1].[Colour] = @p__linq__0)
AND ( NOT ([Extent1].[Colour] IS NULL
OR @p__linq__0 IS NULL)))
OR (([Extent1].[Colour] IS NULL)
AND (@p__linq__0 IS NULL)))
AND ((([Extent1].[Size] = @p__linq__1)
AND ( NOT ([Extent1].[Size] IS NULL
OR @p__linq__1 IS NULL)))
OR (([Extent1].[Size] IS NULL) AND (@p__linq__1 IS NULL)))',
N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',
@p__linq__0=N'9999 is the name of this colour',
@p__linq__1=N'99 is the name is this size'
go
exec sp_executesql N'SELECT
[Extent1].[WidgetId] AS [WidgetId],
[Extent1].[Size] AS [Size],
[Extent1].[Colour] AS [Colour]
FROM [dbo].[Widget] AS [Extent1]
WHERE ((([Extent1].[Size] = @p__linq__0)
AND ( NOT ([Extent1].[Size] IS NULL
OR @p__linq__0 IS NULL)))
OR (([Extent1].[Size] IS NULL)
AND (@p__linq__0 IS NULL)))
AND ((([Extent1].[Colour] = @p__linq__1)
AND ( NOT ([Extent1].[Colour] IS NULL
OR @p__linq__1 IS NULL)))
OR (([Extent1].[Colour] IS NULL)
AND (@p__linq__1 IS NULL)))',
N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',
@p__linq__0=N'99 is the name is this size',
@p__linq__1=N'9999 is the name of this colour'
您可能还会像我一样发现,如果您重新运行虚拟数据插入,现在有 200 万行,问题就会消失。
最佳答案
问题的核心不是“为什么顺序对 LINQ 很重要?”。 LINQ 只是按字面翻译而不重新排序。真正的问题是“为什么这两个 SQL 查询有不同的性能?”。
我能够通过仅插入 10 万行来重现该问题。在这种情况下,优化器中的一个弱点被触发:由于复杂的条件,它无法识别它可以对 Colour
进行查找。在第一个查询中,优化器确实识别了模式并创建了索引查找。
没有语义上的理由说明为什么会这样。即使在 NULL
上查找时,也可以在索引上查找。这是优化器中的弱点/错误。这是两个计划:
EF 试图在这里提供帮助,因为它假定列和过滤器变量都可以为空。在这种情况下,它会尝试为您提供匹配项(根据 C# 语义,这是正确的做法)。
我尝试通过添加以下过滤器来撤消它:
Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL
希望优化器现在使用该知识来简化复杂的 EF 过滤器表达式。它没有设法这样做。如果此方法有效,则可以将相同的筛选器添加到 EF 查询中,从而提供简单的修复。
以下是我按照您应该尝试的顺序推荐的修复方法:
Colour, Size
和/或 Size, Colour
。他们还消除了他们的问题。INTERSECT
/Queryable.Intersect
组合过滤器。这通常会导致不同的计划形状。所有这些都是解决方法,而不是根本原因修复。
最后,我对这里的 SQL Server 和 EF 都不满意。两种产品都应该是固定的。唉,他们可能不会,你也等不及了。
这是索引脚本:
CREATE NONCLUSTERED INDEX IX_Widget_Colour_Size ON dbo.Widget
(
Colour, Size
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
CREATE NONCLUSTERED INDEX IX_Widget_Size_Colour ON dbo.Widget
(
Size, Colour
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
关于c# - LINQ 查询子句的顺序是否会影响 Entity Framework 的性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17323547/
这是一个非常笼统的问题,我希望我能答对。 我正在研究 SSL/TLS 重新协商并已阅读了一些内容。这是我从阅读中了解到的内容: 从 SSL/TLS 重新协商的角度来看,客户端分为两个主要组,打补丁的和
第一个屏幕是艺术的细节。当我向上滚动时,标题将是 alpha。我点击另一个“艺术”到另一个细节 UI,然后按回到 Previous UI。之前的UI标题是黑色的,怎么变透明了。 布局:
想知道 mv 对基表的影响。它会减慢基表的速度吗?它什么时候开始写入 mv,就像同时写入基表和 mv 一样? 如果我有 local_quorum 的 CL 且 RF=3,客户端是否必须等到写入 mv
似乎在任何地方都找不到太多关于此问题的帮助,所以我想我会在这里尝试。 我正在尝试制作一个简单的 for 循环,当我将鼠标悬停在 html 卡上时,它会隐藏卡中的一些文本。该卡有一个简单的名字和姓氏,我
我有一个程序每帧运行 tick() 方法。我希望一个对象根据设定的重力常数下落,因此我创建了一个 Ball 对象,该对象会将其位置更新为前一帧的位置减去 y 速度。每个刻度 y 速度都会减少重力常数。
我的 KeyHandler 在这里: private void KeyHandler(java.awt.event.KeyEvent evt) {
我有一个方法,其中使用了很多其他类,包括链接列表、队列和堆栈。在我的方法中,我有一个 for 循环,我想在其中弹出堆栈(方便地命名为 s)并将队列(方便地命名为 q)出队到 s1 和 q1。由于某种原
我有一个 JTree 节点数组和另一个自定义对象的相应数组。 我想要什么:当选择 JTree 的节点时,相应对象(其数组中索引与节点数组中所选节点索引相同的对象)的字段填充 JLabels。 我被困在
我知道浏览器完成了处理客户端脚本(Javascript、JQuery 等)的所有工作,但想知道在性能方面是否还有其他重要因素(网络速度、客户端计算机速度、服务器环境) 如果它完全依赖于浏览器(类型和版
我有一个 Android 服务在后台运行,它将使用以下代码: while(true) { ServerSocket server = new ServerSocket(1234); Socke
对JQM有以下疑惑: 1.如果我们在单独的 html 文件中使用重复的 id,对 jquery mobile 有什么影响。 假设我们在单独的 html 文件中有重复的 id,但如果我们不使用该 id
我正在尝试更新两个(inventory、sold)MySQL 表的表库存。 假设我们正在处理的 sku 是 BT888-16 UPDATE inventory JOIN sold ON invento
我使用这种方法来更改我的表格单元格值, 它在 jtable 上改变但在文本文件上没有改变! public class user_AllBooks extends AbstractTableModel
我想在向表中插入数据时创建一个 MYSQL 存储过程,数据也会被插入到其他服务器表中。 我知道这在 ORACLE 数据库中是可能的,但我不知道它是否适用于 MYSQL。 有什么办法吗? 最佳答案 是的
我在 css 方面非常糟糕,只能靠 SO 答案来解决 - 但是我找不到针对这个特定问题的任何解释。 我有一个表单,其中包含一个 textarea 和一个 button(input/submit),仅此
我在一个元素上有动画,但它的移动也会影响 sibling 。如何在不影响兄弟元素的情况下仅在元素上使用动画? 问题示例: function animateSearch() { $('.glyph
我试图在我的 ViewController 中的 UIView 的所有四个边上建立一个阴影 — 在我通过 Xcode 向 UIView 添加约束之前,它工作得很好。我怎样才能使 UIView 的阴影显
自从我使用 JavaScript 以来已经有一段时间了 - 在获得证书之后我开始学习 Perl 并从那时起就一直使用它。我只是想重新开始使用 JS,我已经写了这个,我想说的是,这是一个简单的小脚本,可
我正在处理一个 HTML 元素,我添加了一个复选框,选中后会高亮显示所有文本输入字段。唯一的问题是一些输入字段在表格内,出于某种原因我无法用我的代码影响它们。任何帮助将不胜感激。 相关代码: HTML
我为 String 类创建了一个小扩展,以便方便地从中删除字符。这是它的样子: mutating func drop(characters chars: [String]) { for c i
我是一名优秀的程序员,十分优秀!