Когда у вас есть запрос или хранимая процедура, требующие настройки производительности, что вы делаете в первую очередь?
Когда у вас есть запрос или хранимая процедура, требующие настройки производительности, что вы делаете в первую очередь?
Ответы:
Вот удобный список вещей, которые я всегда даю тем, кто спрашивает меня об оптимизации.
В основном мы используем Sybase, но большинство советов применимы ко всем.
SQL Server, например, поставляется с множеством инструментов для мониторинга / настройки производительности, но если у вас нет ничего подобного (а может быть, даже если есть), я бы подумал о следующем ...
99% проблем, которые я видел, вызваны объединением слишком большого количества таблиц . Чтобы исправить это, нужно выполнить половину соединения (с некоторыми таблицами) и кэшировать результаты во временной таблице. Затем выполните остальную часть запроса, присоединившись к этой временной таблице.
#temp
таблицы могут работать намного лучше, чем @table
переменные с большими объемами (тысячи строк).Немного не по теме, но если вы контролируете эти вопросы ...
Высокий уровень и большое влияние.
CREATE INDEX
Убедитесь, что для ваших предложений WHERE
и есть индексы JOIN
. Это значительно ускорит доступ к данным.
Если ваша среда представляет собой витрину или хранилище данных, индексов должно хватить почти для любого мыслимого запроса.
В транзакционной среде количество индексов должно быть меньше, а их определения - более стратегическими, чтобы обслуживание индексов не перетягивало ресурсы. (Техническое обслуживание Индекса , когда листы индекса должны быть изменены , чтобы отразить изменения в базовой таблице, как и с INSERT, UPDATE,
и DELETE
операциями.)
Кроме того, помните о порядке полей в индексе - чем более избирательно (с большей мощностью) поле, тем раньше в индексе оно должно появиться. Например, вы запрашиваете подержанные автомобили:
SELECT i.make, i.model, i.price
FROM dbo.inventory i
WHERE i.color = 'red'
AND i.price BETWEEN 15000 AND 18000
Цена обычно имеет более высокую мощность. В наличии может быть всего несколько десятков цветов, но вполне возможно, что запрашиваемые цены будут тысячи.
Из этих вариантов индекса idx01
обеспечивает более быстрый путь для удовлетворения запроса:
CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)
Это связано с тем, что меньшее количество автомобилей будет соответствовать цене, чем выбор цвета, что дает механизму запросов гораздо меньше данных для анализа.
Известно, что у меня есть два очень похожих индекса, различающихся только порядком полей для ускорения запросов (имя, фамилия) в одном и (фамилия, имя) в другом.
Недавно я усвоил трюк: SQL Server может обновлять локальные переменные, а также поля в операторе обновления.
UPDATE table
SET @variable = column = @variable + otherColumn
Или более читабельная версия:
UPDATE table
SET
@variable = @variable + otherColumn,
column = @variable
Я использовал это для замены сложных курсоров / объединений при выполнении рекурсивных вычислений, а также значительно повысил производительность.
Вот подробности и пример кода, который позволил добиться фантастических улучшений в производительности: http://geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal. ASPX
Предполагая, что здесь MySQL, используйте EXPLAIN, чтобы узнать, что происходит с запросом, убедитесь, что индексы используются максимально эффективно, и попытайтесь исключить сортировку файлов. Высокая производительность MySQL: оптимизация, резервное копирование, репликация и многое другое - отличная книга по этой теме, как и блог о производительности MySQL .
@Terrapin есть еще несколько различий между isnull и coalesce, которые стоит упомянуть (помимо соответствия ANSI, что для меня очень важно).
Иногда в SQL Server, если вы используете OR в предложении where, это действительно снижает производительность. Вместо использования ИЛИ просто сделайте два выбора и объедините их вместе. Вы получаете те же результаты на 1000-кратной скорости.
Обычно я начинаю с объединений - я выбиваю каждое из них из запроса по одному и повторно запускаю запрос, чтобы понять, есть ли конкретное соединение, с которым у меня проблемы.
Во все мои временные таблицы я люблю добавлять уникальные ограничения (где это необходимо) для создания индексов и первичные ключи (почти всегда).
declare @temp table(
RowID int not null identity(1,1) primary key,
SomeUniqueColumn varchar(25) not null,
SomeNotUniqueColumn varchar(50) null,
unique(SomeUniqueColumn)
)
Я взял за привычку всегда использовать переменные связывания. Возможно, переменные связывания не помогут, если СУБД не кэширует операторы SQL. Но если вы не используете переменные связывания, у РСУБД нет возможности повторно использовать планы выполнения запросов и проанализированные операторы SQL. Экономия может быть огромной: http://www.akadia.com/services/ora_bind_variables.html . Я работаю в основном с Oracle, но Microsoft SQL Server работает почти так же.
По моему опыту, если вы не знаете, используете ли вы переменные связывания, скорее всего, нет. Если ваш язык приложения не поддерживает их, найдите тот, который поддерживает. Иногда вы можете исправить запрос A, используя переменные связывания для запроса B.
После этого я разговариваю с нашим администратором баз данных, чтобы выяснить, что причиняет РСУБД наибольшую боль. Обратите внимание, что вы не должны спрашивать «Почему этот запрос медленный?» Это все равно, что попросить врача удалить вам аппендикс. Конечно, проблема может быть в вашем запросе, но так же вероятно, что что-то еще не так. Как разработчики, мы склонны мыслить строчками кода. Если линия медленная, исправьте эту линию. Но СУБД - это действительно сложная система, и ваш медленный запрос может быть признаком гораздо более серьезной проблемы.
Слишком много советов по настройке SQL - кумиры культа карго. В большинстве случаев проблема не связана или минимально связана с используемым синтаксисом, поэтому обычно лучше использовать максимально чистый синтаксис. Затем вы можете начать искать способы настройки базы данных (а не запроса). Изменяйте синтаксис только тогда, когда это не удается.
Как и при любой настройке производительности, всегда собирайте значимую статистику. Не используйте время настенных часов, если это не настраивается пользователем. Вместо этого посмотрите на такие вещи, как время ЦП, извлеченные строки и блоки, считанные с диска. Слишком часто люди оптимизируются не для того.
Выполнение запроса с использованием WITH (NoLock) для меня в значительной степени является стандартной операцией. Любой, кто поймал выполнение запросов к таблицам размером в десятки гигабайт, без этого вынимается и расстреливается.
Преобразуйте запросы NOT IN в LEFT OUTER JOINS, если это возможно. Например, если вы хотите найти все строки в Table1, которые не используются внешним ключом в Table2, вы можете сделать это:
SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
SELECT Table1ID
FROM Table2)
Но вы получите гораздо лучшую производительность с этим:
SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null
@ DavidM
Предполагая, что здесь MySQL, используйте EXPLAIN, чтобы узнать, что происходит с запросом, убедитесь, что индексы используются максимально эффективно ...
В SQL Server план выполнения дает вам то же самое - он сообщает вам, какие индексы используются и т. Д.
Не обязательно трюк с производительностью SQL как таковой, но определенно связанный:
Хорошей идеей было бы использовать memcached там, где это возможно, поскольку это было бы намного быстрее, просто извлекая предварительно скомпилированные данные непосредственно из памяти, а не из базы данных. Также есть разновидность MySQL, в которую встроен memcached (сторонний).
Убедитесь, что длина вашего индекса как можно меньше. Это позволяет БД читать больше ключей из файловой системы за раз, тем самым ускоряя ваши соединения. Я предполагаю, что это работает со всеми БД, но я знаю, что это конкретная рекомендация для MySQL.
Я ищу:
SET NOCOUNT ON
Обычно первая строка внутри моих хранимых процедур, если мне действительно не нужно использовать @@ROWCOUNT
.
В SQL Server используйте директиву nolock. Это позволяет команде select завершиться без ожидания - обычно заканчиваются другие транзакции.
SELECT * FROM Orders (nolock) where UserName = 'momma'
Удалите курсоры везде, где они не нужны.
Удалите вызовы функций в Sprocs, где многие строки будут вызывать функцию.
Мой коллега использовал вызовы функций (например, получение lastlogindate из идентификатора пользователя) для возврата очень широких наборов записей.
Для оптимизации я заменил вызовы функций в sproc на код функции: у меня время работы многих sproc уменьшилось с> 20 секунд до <1.
Я люблю использовать
isnull(SomeColThatMayBeNull, '')
Над
coalesce(SomeColThatMayBeNull, '')
Когда мне не нужна поддержка нескольких аргументов, которую дает вам coalesce.
http://blog.falafel.com/2006/04/05/SQLServerArcanaISNULLVsCOALESCE.aspx
Не ставьте перед именами хранимых процедур префикс sp_, потому что все системные процедуры начинаются с sp_, и SQL Server будет труднее искать вашу процедуру при ее вызове.
set transaction isolation level read uncommitted
Предотвращает мертвые блокировки, когда целостность транзакции не является абсолютно необходимой (что обычно верно)
Я всегда сначала использую SQL Profiler (если это хранимая процедура с большим количеством уровней вложенности) или планировщик выполнения запросов (если это несколько операторов SQL без вложенности). В 90% случаев вы можете сразу найти проблему с помощью одного из этих двух инструментов.