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+ базах данных на разных эластичных пулах для обслуживания индекса. Надеюсь, другие считают эту информацию полезной, как мы.