我有两个表(在 Azure SQL 数据库中):
CREATE TABLE [dbo].[Action]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Action] [varchar](7) NOT NULL,
[Resource] [varchar](16) NOT NULL,
[Timestamp] [datetime] NOT NULL
)
ALTER TABLE [dbo].[Action]
ADD CONSTRAINT [PK_Action]
PRIMARY KEY CLUSTERED ([ID] ASC)
和
CREATE TABLE [dbo].[ResourceScope]
(
[Resource] [varchar](16) NOT NULL,
[Scope] [varchar](100) NOT NULL
)
ALTER TABLE [dbo].[ResourceScope]
ADD CONSTRAINT [PK_ResourceScope]
PRIMARY KEY CLUSTERED ([Scope] ASC, [Resource] ASC)
Action
表包含约 1500 万个操作,涉及约 150 万个独特资源。 ResourceScope
表包含大约 10K 行,具有独特的资源和几个不同范围之一。
API 需要使用以下查询对
Action
表进行分页,其中 <id>
和 <scope>
都是可变的,但页面大小始终相同。
SELECT TOP 101
[Action].[ID],
[Action].[Action],
[Action].[Resource],
[Action].[Timestamp]
FROM
[Action]
JOIN
[ResourceScope] ON [ResourceScope].[Resource] = [Action].[Resource]
WHERE
[Action].[ID] <= <id>
AND [ResourceScope].[Scope] = <scope>
ORDER BY
[Action].[ID] DESC
在对特定范围内的数据进行分页时,大约 95% 的查询大约需要 15 毫秒,但其余 5% 的查询需要 500 毫秒到 4000 毫秒之间的任何时间。慢速查询始终使用完全相同的 ID/范围组合,我可以将范围缩小到一些导致查询变慢的确切 ID。
查询慢的 id 与快的 id 具有完全相同的查询计划,但慢的 id 在
ResourceScope
主键上执行大量聚集索引查找,而快的 id 仅执行几次执行(主要是大约 100 次执行)该聚集索引在该索引上查找。
慢速 id 计划示例:
https://www.brentozar.com/pastetheplan/?id=ryScGHekR
快速 ID 计划示例:
https://www.brentozar.com/pastetheplan/?id=B1c6MrxyA
当我为特定范围添加索引视图(如下所示)并通过该视图进行分页时,5% 的慢查询消失了。
CREATE VIEW [dbo].[DemoSetAction]
WITH SCHEMABINDING
AS
SELECT
[dbo].[Action].[ID],
[dbo].[Action].[Action],
[dbo].[Action].[Resource],
[dbo].[Action].[Timestamp]
FROM
[dbo].[Action]
JOIN
[dbo].[ResourceScope] ON [dbo].[ResourceScope].[Resource] = dbo.[Action].[Resource]
WHERE
[dbo].[ResourceScope].[Scope] = 'demo-set'
CREATE UNIQUE CLUSTERED INDEX IDX_DemoSet ON [dbo].[DemoSetAction] (ID);
视图分页查询:
SELECT TOP 101
[DemoSetAction].[ID],
[DemoSetAction].[Action],
[DemoSetAction].[Resource],
[DemoSetAction].[Timestamp]
FROM
[DemoSetAction]
WHERE
[Action].[ID] <= <id>
ORDER BY
[Action].[ID] DESC
由于可以动态添加范围(但预计不会超过约 25 个唯一范围),因此我不希望为每个范围创建索引视图。
两个问题:
为什么不在视图上创建列存储索引?
试试这个:
CREATE TABLE [dbo].[Action]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Action] [varchar](7) NOT NULL,
[Resource] [varchar](16) NOT NULL,
[Timestamp] [datetime] NOT NULL
)
GO
ALTER TABLE [dbo].[Action]
ADD CONSTRAINT [PK_Action]
PRIMARY KEY CLUSTERED ([ID] ASC);
GO
CREATE TABLE [dbo].[ResourceScope]
(
[Resource] [varchar](16) NOT NULL,
[Scope] [varchar](100) NOT NULL
)
GO
ALTER TABLE [dbo].[ResourceScope]
ADD CONSTRAINT [PK_ResourceScope]
PRIMARY KEY CLUSTERED ([Scope] ASC, [Resource] ASC);
GO
CREATE VIEW [dbo].[DemoSetAction]
WITH SCHEMABINDING
AS
SELECT
[dbo].[Action].[ID],
[dbo].[Action].[Action],
[dbo].[Action].[Resource],
[dbo].[Action].[Timestamp],
[dbo].[ResourceScope].[Scope]
FROM
[dbo].[Action]
JOIN
[dbo].[ResourceScope] ON [dbo].[ResourceScope].[Resource] = dbo.[Action].[Resource]
GO
CREATE UNIQUE CLUSTERED INDEX IDX_DemoSet ON [dbo].[DemoSetAction] (ID, [Scope]);
GO
CREATE COLUMNSTORE INDEX XC_DemoSet ON [dbo].[DemoSetAction] ([ID], [Action], [Resource], [Timestamp], [Scope]);
GO