【问题标题】:SQL Performance - Indexed View VS Multi-Column IndexSQL 性能 - 索引视图 VS 多列索引
【发布时间】:2018-11-25 07:34:36
【问题描述】:

我有 2 个设置显示不同的性能,我想了解原因。 我必须写下很多信息,以便在上下文中有意义。

TLTR:为什么我失去了多列索引的对数可扩展性?

桌子:

CREATE TABLE Schema1.Item
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    UniqueName VARCHAR(20) NOT NULL UNIQUE,
    GroupId INT NOT NULL FOREIGN KEY REFERENCES Schema1.Group(Id),
    Category VARCHAR(200),
    Properties VARCHAR(max)
);

如果属性名称+属性值,则最后一列“属性”包含 JSON 字典。那里的哪些属性特定于 GroupId。

测试数据:

  • 由 100 万个项目组成
  • 分布在 20 个组中(因此每组 50000 个项目)
  • 包含 10 个类别(因此每组每个类别 5000 个项目)

这是表越大性能越低的索引:

CREATE NONCLUSTERED INDEX IX_GroupId_Category 
ON [Schema1].[Item] (GroupId, Category) 
INCLUDE(Id, UniqueName, Properties)

所以查询可以如下所示:

SELECT TOP (1000) *   
FROM [Schema1].[Item]
WHERE GroupId = 2
  AND Category = 'Category4'
  AND JSON_VALUE(Properties, '$."PropertyName"') LIKE '%PropertyValue%'

但我想讨论的只是这个查询,因为最终这个查询之后的所有内容总是

SELECT TOP (1000) *   
FROM [Schema1].[Item]
WHERE GroupId = 2
  AND Category = 'Category4'

执行计划基本上只包含 100% Index Seek,估计 + 实际行数 = 1000(如预期)。这里一切看起来都很好。

但是对于 1.000.000 个项目,此查询仍然需要 2-3 秒 才能完成(没有查询缓存)。对于 100.000 个项目,这已经

这似乎违反了索引的对数可扩展性的逻辑?即使有我非常大的索引叶子(因为它们包含带有nvarchar(max) 的整个列,通常约为 500 字节),100.000 和 1.000.000 项之间仍然不应该有这么大的差异吗?

所以我接下来尝试的是创建一个索引视图

  • GroupId 上的过滤器(因此它最多有 50.000 行)
  • 并且在 Category 上有一个索引(+包括所有列,与之前相同)

对于这个视图,这样的查询:

SELECT TOP (1000) *   
FROM [Schema1].[Item_ViewGroupId1]    
WHERE Category = 'Category4'

只需要

谁能向我解释一下为什么这两种实现之间存在如此大的差异?

我错过了什么吗?


编辑: 问题似乎与物理读取有关:

  • 慢:表“项目”。扫描计数 1,逻辑读取 362,物理读取 148,预读读取 547,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
  • 快速:表“项目”。扫描计数 1,逻辑读取 362,物理读取 0,预读读取 264,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0

似乎平均而言,对视图的查询需要较少的物理读取?

这是否意味着我只依赖于服务器正在缓存的内容?有什么办法可以改善吗?

【问题讨论】:

  • 您的 WHERE 子句过滤 Category 和 ClassId 但索引位于 GroupId (未在查询中使用)和 Category 上,因此它没有用。尝试在 Caegory 和 ClassId 上创建索引。我希望这可以提高性能,但不如索引视图那么快。请注意,您需要 ORDER BYTOP。否则,返回的行是随机的。
  • 当您使用 100,000 个项目进行测试时,是否返回了 1000 行?与每组 20 个组和 10 个类别一样,它可能只有 500 行。如果您 SET STATISTICS IO ON,查询是否显示逻辑读取的差异?
  • @DanGuzman:这只是一个错字,我修正了它。它正在搜索正确的索引。而且我对任何订单都不感兴趣。
  • @DavidBrowne-Microsoft:这两个查询的结果是相同的,除了查询的持续时间。我将研究逻辑读取。
  • @DavidBrowne-Microsoft:我做了一些进一步的测试,似乎物理读取是问题的原因?它们似乎很少出现在我的观点上。慢表“项目”。扫描计数 1,逻辑读取 362,物理读取 148,预读读取 547,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。FAST 表“项目”。扫描计数 1,逻辑读取 362,物理读取 0,预读读取 264,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。

标签: sql sql-server azure-sql-database


【解决方案1】:

如果plan是一样的,逻辑IO也是一样的,那么经过的时间也是一样的,除非有等待,比如IO等待,锁等待等。表,您的页面并未全部缓存。

查询存储跟踪每个查询和每个计划的等待,因此您可以通过以下方式进行检查:

select qt.query_sql_text, p.plan_id, ws.wait_category, ws.wait_category_desc, ws.avg_query_wait_time_ms
from sys.query_store_query q
left join sys.query_store_query_text qt
  on q.query_text_id= qt.query_text_id
left join sys.query_store_plan p
  on q.query_id = p.plan_id
left join sys.query_store_wait_stats ws
 on p.plan_id = ws.plan_id
order by q.query_id, p.plan_id, ws.wait_category, ws.wait_category_desc, ws.avg_query_wait_time_ms desc

这是否意味着我只依赖于服务器正在缓存的内容? 是的。您的查询性能将始终取决于您的数据是否被缓存。

有什么办法可以改善吗?

SQL Server 将在页面缓存中保留最常用的页面,并且要缓存更多数据,您可以增加可用内存量(通过增加 DTU 或 vCore),或增加适合的行数页面。您可以在这里尝试的一件事是COMPRESS JSON 数据和DECOMPRESS 在需要时使用它。这将缓存更多数据,但代价是读取时需要额外的 CPU。

【讨论】:

    猜你喜欢
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    • 2010-11-08
    • 2019-10-03
    • 1970-01-01
    • 2013-04-23
    相关资源
    最近更新 更多