SQL Azure Query Performance - невероятно медленный даже с настроенными запросами

Вот базовый запрос, который опирается на два некластеризованных индекса:

SELECT cc.categoryid, count(*) from company c
INNER JOIN companycategory cc on cc.companyid = c.id
WHERE c.placeid like 'ca_%'
GROUP BY cc.categoryid order by count(*) desc

Когда та же самая база данных размещается на SQL Server 2008, практически на любом оборудовании, она возвращает < 500 мс. Даже при очистке буферов кэша:

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

... это все равно возвращается через ~ 1 секунду на традиционном SQL.

На Azure требуется возврат приблизительно каждые 3,5 секунды.

Некоторые статьи, похоже, показывают, что люди в целом довольны производительностью запросов в SQL Azure. И все же здесь базовый сценарий, когда "очевидная" настройка была исчерпана, и о проблемах с латентностью в сети не говорится. Это просто очень медленно при работе с большими таблицами (компания имеет рейтинг 1.2M, места имеют 7.5K).

Общий размер базы данных не более 4 ГБ. Выбор варианта "Web" и "Enterprise", похоже, не имеет большого значения.

Что мне не хватает?

Это только базовый пример, он только ухудшается с более сложными запросами, все из которых были просмотрены, настроены и хорошо работают -premise.

Здесь план выполнения:

  |--Sort(ORDER BY:([Expr1004] DESC))
       |--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(int,[Expr1007],0)))
            |--Hash Match(Aggregate, HASH:([cc].[CategoryId]), RESIDUAL:([XX].[dbo].[CompanyCategory].[CategoryId] as [cc].[CategoryId] = [XX].[dbo].[CompanyCategory].[CategoryId] as [cc].[CategoryId]) DEFINE:([Expr1007]=COUNT(*)))
                 |--Hash Match(Inner Join, HASH:([c].[Id])=([cc].[CompanyId]))
                      |--Index Scan(OBJECT:([XX].[dbo].[Company].[IX_Company_PlaceId] AS [c]),  WHERE:([XX].[dbo].[Company].[PlaceId] as [c].[PlaceId] like N'ca_%'))
                      |--Index Scan(OBJECT:([XX].[dbo].[CompanyCategory].[IX_CompanyCategory_CompanyId] AS [cc]))

И вот статистика:

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
   CPU time = 14 ms, elapsed time = 14 ms.

(789 row(s) affected)

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CompanyCategory'. Scan count 1, logical reads 5183, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Company'. Scan count 1, logical reads 8710, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 3328 ms,  elapsed time = 3299 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

Определения индексов следующие:

CREATE NONCLUSTERED INDEX [IX_Company_PlaceId] ON [dbo].[Company] 
(
    [PlaceId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
GO

CREATE NONCLUSTERED INDEX [IX_CompanyCategory_CompanyId] ON [dbo].[CompanyCategory] 
(
    [CompanyId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
GO

ALTER TABLE [dbo].[Company] ADD  CONSTRAINT [PK_Company_Id] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
GO

Ответ 1

Кажется, что они используют одно CPU ядро ​​для вашего запроса, пока на вашем компьютере запрос, вероятно, распараллеливается (все операции, используемые запросом, распараллеливаются).

Тем не менее, сканирование индексов используется для предиката LIKE по какой-либо причине, в то время как поиск индекса может быть достаточным.

Попробуйте использовать это явное условие вместо LIKE:

c.placeid >= 'ca'
AND c.placeid < 'cb'

и посмотрите, изменит ли он план на Index Seek на IX_CompanyPlaceId.

Ответ 2

Несколько вещей:

  • Обновлены ли данные на Azure? Я немного опасаюсь этого Hash Match для таблицы строк 1.2M.
  • Есть ли у Azure авто статистики? Если нет, ваша локальная база данных может иметь гораздо больше информации, которую SQL Azure не может использовать для выбора оптимального плана запроса.
  • Индекс c.placeid для некоторых статистических данных о нем
  • Почему строка c.placeid? Выполняется ли это до companyid и c.id? Я думаю, именно поэтому у вас есть Hash Match - попробуйте присоединиться к целым суррогатным клавишам.

Ответ 3

Я размещаю эту ссылку для обслуживания индекса Azure SQL Database, поскольку обслуживание индекса по-прежнему нуждается в помощи.

https://blogs.msdn.microsoft.com/azuresqldbsupport/2016/07/03/how-to-maintain-azure-sql-indexes-and-statistics/

Мы используем runbook для выполнения в наших 350+ базах данных на разных эластичных пулах для обслуживания индекса. Надеюсь, другие считают эту информацию полезной, как мы.