【问题标题】:Query plan caching and performance查询计划缓存和性能
【发布时间】:2011-03-05 18:18:06
【问题描述】:

如果您愿意,可以阅读背景故事,在将我的非聚集索引更改为聚集索引之后,事情开始运行得更快了。不过,初始查询计划需要 2-3 秒的问题仍然存在。但是,keep plan 查询提示改进了很多。

背景故事

我有一个倒排索引,用于存储我希望在简单搜索实现中查找的内容(尽管搜索并不简单)。

当我输入这样的“ma br”查询时,它会创建这个 SQL

SELECT EntityType, EntityID FROM InvertedIndex WHERE Term LIKE @p0
INTERSECT
SELECT EntityType, EntityID FROM InvertedIndex WHERE Term LIKE @p1
--@p0 String --'ma%'
--@p1 String --'br%'

对于每个搜索词,都有另一个 INTERSECTSELECT

而且在大多数情况下,这非常有效。我已经正确索引了Term 列,并检查了执行计划是否存在潜在瓶颈。目前,该索引包含大约 150,000 行,并且搜索按预期即时发生。

但有点烦人的是,通常某个等级的第一个查询需要更长的时间来执行。我敢打赌这是因为查询优化器正在重新评估执行计划。但是我该怎么处理呢?我对服务器运行的查询越多,它停止的次数就越少,但是每个其他查询都需要大约 2-3 秒的时间。这没什么大不了的,但有时它会更长,我只是看不到这是从哪里来的或如何处理它。应该是闪电般的速度。

编辑

架构如下所示:

CREATE TABLE InvertedIndex (
    Term varchar(255) NOT NULL,
    Ordinal tinyint NOT NULL,
    EntityType tinyint NOT NULL,
    EntityID int NOT NULL
)

这两个索引是:

CREATE NONCLUSTERED INDEX IX_InvertedIndex ON InvertedIndex (Term) 
INCLUDE (Ordinal, EntityType, EntityID)

CREATE NONCLUSTERED INDEX IX_InvertedIndex_Reverse ON InvertedIndex (EntityType, EntityID) 

这个东西留下来了,当索引(InvertedIndex)需要更新和完全重建时会发生插入和删除操作,这会影响QUERY PLAN的使用吗?

这是一个完整查询的示例,它真的很慢 atm,3-5 秒,我不知道为什么...ORDER BY 子句旨在为匹配某个位置的单词提供更高的排序顺序(发生在结果集中的第一名)但是对于每个搜索词,这会变得指数级地变慢。

WITH Search AS (
    SELECT EntityType, EntityID FROM InvertedIndex WHERE Term LIKE @p0
    INTERSECT
    SELECT EntityType, EntityID FROM InvertedIndex WHERE Term LIKE @p1
    INTERSECT
    SELECT EntityType, EntityID FROM InvertedIndex WHERE Term LIKE @p2
)
SELECT p.PersonID
, p.FullName
, p.Email
, p.MobilePhone
, p.HomeAddress
, p.HomeCity
FROM Search AS s
INNER JOIN Person AS p ON p.PersonID = s.EntityID AND s.EntityType = @pPersonEntityType
ORDER BY (CASE WHEN @p3 IN (SELECT Ordinal FROM InvertedIndex WHERE Term LIKE @p0 AND EntityID = s.EntityID AND EntityType = s.EntityType) THEN 0 ELSE 1 END) + (CASE WHEN @p4 IN (SELECT Ordinal FROM InvertedIndex WHERE Term LIKE @p1 AND EntityID = s.EntityID AND EntityType = s.EntityType) THEN 0 ELSE 1 END) + (CASE WHEN @p5 IN (SELECT Ordinal FROM InvertedIndex WHERE Term LIKE @p2 AND EntityID = s.EntityID AND EntityType = s.EntityType) THEN 0 ELSE 1 END)
@p0 String --'ma%'
@p1 String --'br%'
@p2 String --'mi%'
@p3 Int32 --1
@p4 Int32 --2
@p5 Int32 --3

上述查询的重点是查找InvertedIndex 中的所有术语,然后,对于每个搜索术语都有一个相交,这就是我希望用来限制搜索的逻辑结合。 Ordinal 表示词在被索引时的原始位置。 InvertedIndex 中的每个条目都代表一个元组,如果搜索词与此 N 元组的某个项目匹配,则认为它是更好的匹配。这就是为什么我需要通过子查询来完成这个时髦的订单。但是真的很慢。

回答

如果我将IX_InvertedIndex 更改为聚集索引,它会将查询速度提高一个数量级(但我不知道为什么):

CREATE CLUSTERED INDEX IX_InvertedIndex ON InvertedIndex (Term) 

【问题讨论】:

  • 这取决于...!表定义是什么,定义了哪些索引?
  • 你有一个主键和唯一的聚集索引?
  • 没有。我不需要它。至少,我是在假设没有理由在这里添加它的情况下进行操作的。我错了吗?
  • 是的,你知道。这是你的问题。还有为什么 SearchIndex 表要引用 InvertedIndex?
  • 已更改名称... 很抱歉让您感到困惑,您能否详细说明您的最后评论?因为我不明白为什么在这种情况下主键很重要。

标签: sql-server-2005 performance tsql sql-execution-plan


【解决方案1】:

如果您要附加 INTERSECT 子句,那么每个查询不同。我怀疑(根据您所说的)最终您对每个数量的 INTERSECT 子句都有一个缓存计划。一旦你有了缓存的计划,它就可以运行了。

您可以尝试plan guides,每个 INTERSECT 子句数量一个。

否则,您始终可以在临时表和自联接中使用单个选择和假脱机结果。不知道它会如何运行或者它是否是个好主意。

您的索引也应该与INCLUDE 一起使用,以便EntityType, EntityID 也可以覆盖

评论后编辑。

您可以尝试KEEP PLANplan forcing too,因为它们相对简单,有助于避免听起来像是重新编译。

如果是 SQL Server 2008,我建议OPTIMISE FOR UNKNOWN

最后,另一个想法:数据类型是否全面匹配?

编辑:您应该将索引更改为 (Term, EntityType, Ordinal, EntityID),不包含任何内容。您在 JOIN 或过滤器中使用 all

您还需要一个主键(Term?Ordinal),它也应该是 unqiue 和 clustered。除了性能差和数据碎片之外,没有一个没有任何优势

并将查询更改为:

WITH Search AS
(
    SELECT Ordinal, EntityType, EntityID FROM InvertedIndex WHERE Term LIKE @p0 AND EntityType = @pPersonEntityType
    INTERSECT
    SELECT Ordinal, EntityType, EntityID FROM InvertedIndex WHERE Term LIKE @p1 AND EntityType = @pPersonEntityType
    INTERSECT
    SELECT Ordinal, EntityType, EntityID FROM InvertedIndex WHERE Term LIKE @p2 AND EntityType = @pPersonEntityType
)
SELECT
    p.PersonID, p.FullName, p.Email, p.MobilePhone, p.HomeAddress, p.HomeCity
FROM
    Search AS s
    INNER JOIN
    Person AS p ON p.PersonID = s.EntityID
    LEFT JOIN
    (SELECT 0 AS Ranking, @p3 AS RankOrdinal) O3
    LEFT JOIN
    (SELECT 0 AS Ranking, @p4 AS RankOrdinal) O4
    LEFT JOIN
    (SELECT 0 AS Ranking, @p5 AS RankOrdinal) O5
ORDER BY    --although, I can't see why you are doing + 
    ISNULL(O3.Ranking, 1) +
    ISNULL(O4.Ranking, 1) +
    ISNULL(O5.Ranking, 1)

【讨论】:

  • 索引包括EntityType、EntityID。但感谢您指出这一点。上个月我不知道。我要调查计划指南。虽然我想补充一点,执行计划的变化似乎比这更频繁。只是更改搜索值,仍然是相同的数字,但不同的值,可能会导致这种突然停止。
  • @John Leidegren:我的意思是计划强制而不是计划指南,抱歉。计划指南适用于您无法更改 SQL 的情况。强制是添加查询提示的东西。
  • 我确实尝试了 OPTIMIZE FOR UNKNOWN,但当它是 SQL 2008 功能时遇到了困难。接下来我会尝试保持计划。那么我应该避免使用计划指南吗?还没试过……
  • 唯一可能的主键是 (Term, Ordinal, EntityType, EntityID),即所有列。我不能将 INTERSECT 与 Ordinal 列一起使用,因为它仅用于相关性,搜索是全局的,实体类型并不总是 Person,在本例中是,所以我无法过滤 Search CTE on实体类型。添加只是为了避免拥有超过 1 个排序列。但这并没有改变查询的性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-19
  • 2018-06-24
相关资源
最近更新 更多