Регресс производительности запросов к базе данных Azure DB / MSSQL 2017 - PullRequest
0 голосов
/ 21 ноября 2018

У меня есть эта довольно простая таблица с 17m записями:

CREATE TABLE [dbo].[LineNumbers](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [LineDescriptionId] [int] NOT NULL,
    [ProtocolId] [int] NULL,
    [Value] [int] NULL,
CONSTRAINT [PK_LineNumbers] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Запрос к таблице с дополнительным объединением работает нормально, если в ней нет ProtocolId:

select top 1
  ln.LineDescriptionId
from LineNumbers ln
join LineDescriptions ld on ld.Id = ln.LineDescriptionId and ld.ProtocolSetId = 25

-- Elapsed time: 00:00:00.1718750

План выполнения: https://www.brentozar.com/pastetheplan/?id=rJV34gvR7

Но когда я пытаюсь добавить ProtocolId в список полей, время запроса резко увеличивается:

select top 1
  ln.ProtocolId
from LineNumbers ln
join LineDescriptions ld on ld.Id = ln.LineDescriptionId and ld.ProtocolSetId = 25

-- Elapsed time: 00:02:19.6464843

План выполнения: https://www.brentozar.com/pastetheplan/?id=SkG-hyDCQ

Кроме того, это работает гладко:

select top 1
  (select ProtocolId from LineNumbers where LineNumbers.Id = ln.Id) as ProtocolId
from LineNumbers ln
join LineDescriptions ld on ld.Id = ln.LineDescriptionId and ld.ProtocolSetId = 25

-- Elapsed time: 00:00:00.1718750

Пробовал эти запросы и варианты в БД Azure и локальном MSSQL 2017. Результаты совпадают.Пока я держу ProtocolId вне списка полей, все в порядке.

Есть ли какая-то ошибка в моей схеме данных (все было создано с помощью миграций Entity Framework)?

CREATE TABLE [dbo].[LineNumbers](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [LineDescriptionId] [int] NOT NULL,
    [ProtocolId] [int] NULL,
    [Value] [int] NULL,
CONSTRAINT [PK_LineNumbers] PRIMARY KEY CLUSTERED 
(
    [Id] 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
/****** Object:  Index [IX_LineNumbers_LineDescriptionId]    Script Date: 21.11.2018 10:47:09 ******/
CREATE NONCLUSTERED INDEX [IX_LineNumbers_LineDescriptionId] ON [dbo].[LineNumbers]
(
    [LineDescriptionId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
/****** Object:  Index [IX_LineNumbers_LineDescriptionId_Value]    Script Date: 21.11.2018 10:47:09 ******/
CREATE NONCLUSTERED INDEX [IX_LineNumbers_LineDescriptionId_Value] ON [dbo].[LineNumbers]
(
    [LineDescriptionId] ASC,
    [Value] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
/****** Object:  Index [IX_LineNumbers_ProtocolId]    Script Date: 21.11.2018 10:47:09 ******/
CREATE NONCLUSTERED INDEX [IX_LineNumbers_ProtocolId] ON [dbo].[LineNumbers]
(
    [ProtocolId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE [dbo].[LineNumbers]  WITH NOCHECK ADD  CONSTRAINT [FK_LineNumbers_LineDescriptions_LineDescriptionId] FOREIGN KEY([LineDescriptionId])
REFERENCES [dbo].[LineDescriptions] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[LineNumbers] CHECK CONSTRAINT [FK_LineNumbers_LineDescriptions_LineDescriptionId]
GO
ALTER TABLE [dbo].[LineNumbers]  WITH NOCHECK ADD  CONSTRAINT [FK_LineNumbers_Protocols_ProtocolId] FOREIGN KEY([ProtocolId])
REFERENCES [dbo].[Protocols] ([Id])
GO
ALTER TABLE [dbo].[LineNumbers] CHECK CONSTRAINT [FK_LineNumbers_Protocols_ProtocolId]
GO

1 Ответ

0 голосов
/ 23 ноября 2018

В конце концов я решил эту проблему, добавив некластеризованный индекс в поле LineNumbers.LineDescriptionId с включением LineNumbers.ProtocolId

CREATE NONCLUSTERED INDEX [IX_LineNumbers_LineDescriptionId_ProtocolId] ON
    [dbo].[LineNumbers]([LineDescriptionId] ASC)

INCLUDE ([ProtocolId])

WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
    SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
    ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

Результат:

SELECT TOP 1
    ln.ProtocolId
FROM LineNumbers ln
JOIN LineDescriptions ld ON ld.Id = ln.LineDescriptionId AND ld.ProtocolSetId = 25

-- Elapsed time: 00:00:00.1403155

План выполнения: https://www.brentozar.com/pastetheplan/?id=Syywn1wRQ

Почему это так работает?

Например, если бы я делал аналогичный сценарий использования с PostgreSQL, тогда вообще не нужно никаких дополнительных индексов (кроме очевидных индексов FK на ProtocolId и LineDescriptionId поля).

...