Любимые приемы настройки производительности [закрыто]


126

Когда у вас есть запрос или хранимая процедура, требующие настройки производительности, что вы делаете в первую очередь?


Вот некоторые приемы оптимизации запросов SQL Server
SQLMenace,

Я согласен, что это неконструктивно и можно поискать в гугле, но почему у него 118 уф ?! :)
FLICKER

Ответы:


114

Вот удобный список вещей, которые я всегда даю тем, кто спрашивает меня об оптимизации.
В основном мы используем Sybase, но большинство советов применимы ко всем.

SQL Server, например, поставляется с множеством инструментов для мониторинга / настройки производительности, но если у вас нет ничего подобного (а может быть, даже если есть), я бы подумал о следующем ...

99% проблем, которые я видел, вызваны объединением слишком большого количества таблиц . Чтобы исправить это, нужно выполнить половину соединения (с некоторыми таблицами) и кэшировать результаты во временной таблице. Затем выполните остальную часть запроса, присоединившись к этой временной таблице.

Контрольный список оптимизации запросов

  • Запустите UPDATE STATISTICS в базовых таблицах
    • Многие системы запускают это как запланированное еженедельное задание.
  • Удалить записи из базовых таблиц (возможно, заархивировать удаленные записи)
    • Попробуйте делать это автоматически один раз в день или раз в неделю.
  • Перестроить индексы
  • Перестроить таблицы (выход / вход данных bcp)
  • Дамп / перезагрузка базы данных (радикально, но может исправить повреждение)
  • Создайте новый, более подходящий индекс
  • Запустите DBCC, чтобы увидеть, возможно ли повреждение базы данных.
  • Замки / тупиковые ситуации
    • Убедитесь, что в базе данных не запущены другие процессы
      • Особенно DBCC
    • Используете ли вы блокировку на уровне строк или страниц?
    • Блокируйте таблицы исключительно перед запуском запроса
    • Убедитесь, что все процессы обращаются к таблицам в одном и том же порядке
  • Правильно ли используются индексы?
    • Объединения будут использовать индекс, только если оба выражения имеют один и тот же тип данных
    • Индекс будет использоваться только в том случае, если первое поле (поля) индекса совпадают в запросе
    • Используются ли кластерные индексы там, где это необходимо?
      • данные диапазона
      • Поле WHERE между значением1 и значением2
  • Маленькие соединения - хорошие соединения
    • По умолчанию оптимизатор будет рассматривать только 4 таблицы за раз.
    • Это означает, что при объединениях с более чем 4 таблицами у него есть хорошие шансы выбрать неоптимальный план запроса.
  • Разбейте соединение
    • Можете ли вы разбить соединение?
    • Предварительно выберите внешние ключи во временную таблицу
    • Сделайте половину соединения и поместите результаты во временную таблицу
  • Вы используете правильную временную таблицу?
    • #tempтаблицы могут работать намного лучше, чем @tableпеременные с большими объемами (тысячи строк).
  • Ведение сводных таблиц
    • Создавайте с помощью триггеров для базовых таблиц
    • Строить ежедневно / ежечасно / и т. Д.
    • Сборка ad-hoc
    • Построить постепенно или разобрать / перестроить
  • Посмотрите, какой план запроса, с помощью SET SHOWPLAN ON
  • Посмотрите, что на самом деле происходит с SET STATS IO ON
  • Форсировать индекс с помощью прагмы: (index: myindex)
  • Установите порядок таблиц с помощью SET FORCEPLAN ON
  • Обнюхивание параметров:
    • Разбить хранимую процедуру на 2
    • вызвать proc2 из proc1
    • позволяет оптимизатору выбрать индекс в proc2, если параметр @parameter был изменен процедурой proc1
  • Можете ли вы улучшить свое оборудование?
  • Во сколько ты бежишь? Есть время поспокойнее?
  • Сервер репликации (или другой непрерывный процесс) работает? Вы можете приостановить это? Запустите его, например. ежечасно?

2
о каком бите вы имеете в виду?
Эй Джей.

2
Это классная штука, но я бы хотел, чтобы у вас были ссылки на некоторые претензии. Например: я никогда не слышал, чтобы оптимизация учитывала только 4 таблицы за раз в соединении. Я не понимаю, как это могло быть правильно. Не могли бы вы дать какие-нибудь ссылки в частности на это? Я бы хотел посмотреть, откуда вы это берете.
SheldonH

19
  1. У вас есть довольно хорошее представление об оптимальном пути выполнения запроса в уме.
  2. Проверяйте план запроса - всегда.
  3. Включите СТАТИСТИКУ, чтобы можно было проверить производительность ввода-вывода и процессора. Сосредоточьтесь на уменьшении этих чисел, а не обязательно времени запроса (поскольку на это могут влиять другие действия, кеш и т. Д.).
  4. Обратите внимание на то, что в оператор входит большое количество строк, но выходит небольшое количество. Обычно индекс помогает, ограничивая количество входящих строк (что экономит чтение с диска).
  5. Сначала сосредоточьтесь на поддереве с наибольшей стоимостью. Изменение этого поддерева часто может изменить весь план запроса.
  6. Общие проблемы, которые я видел:
    • Если объединений много, иногда Sql Server выбирает развертывание объединений, а затем применяет предложения WHERE. Обычно это можно исправить, переместив условия WHERE в предложение JOIN или производную таблицу со встроенными условиями. Представления могут вызывать те же проблемы.
    • Неоптимальные соединения (LOOP vs HASH vs MERGE). Мое практическое правило - использовать соединение LOOP, когда в верхней строке очень мало строк по сравнению с нижней, MERGE, когда наборы примерно равны и упорядочены, и HASH для всего остального. Добавление подсказки о соединении позволит вам проверить свою теорию.
    • Обнюхивание параметров. Если вы сначала запустили сохраненную процедуру с нереалистичными значениями (например, для тестирования), то кешированный план запроса может быть неоптимальным для ваших производственных значений. Повторный запуск WITH RECOMPILE должен проверить это. Для некоторых сохраненных процессов, особенно тех, которые имеют дело с диапазонами разного размера (скажем, все даты между сегодняшним днем ​​и вчера, что повлечет за собой поиск по индексу, или все даты между прошлым годом и этим годом), что было бы лучше с сканированием индекса. ) вам, возможно, придется каждый раз запускать его С РЕКОМЕНДАЦИЕЙ.
    • Плохой отступ ... Хорошо, поэтому у Sql Server нет проблем с этим, но я уверен, что невозможно понять запрос, пока я не исправлю форматирование.

1
+1 за включение плохих отступов. Форматирование - ключ к успеху! :)
mwigdahl

18

Немного не по теме, но если вы контролируете эти вопросы ...
Высокий уровень и большое влияние.

  • Для сред с большим количеством операций ввода-вывода убедитесь, что ваши диски предназначены для RAID 10 или RAID 0 + 1 или какой-либо вложенной реализации raid 1 и raid 0.
  • Не используйте диски менее 1500K.
  • Убедитесь, что ваши диски используются только для вашей базы данных. IE без логирования нет ОС.
  • Отключите автоматический рост или аналогичную функцию. Позвольте базе данных использовать все ожидаемое хранилище. Не обязательно то, что сейчас используется.
  • разработать схему и индексы для запросов типов.
  • если это таблица типов журнала (только вставка) и она должна быть в БД, не индексируйте ее.
  • если вы занимаетесь отчетностью (сложный выбор с множеством объединений), вам следует подумать о создании хранилища данных со схемой «звезда» или «снежинка».
  • Не бойтесь копировать данные в обмен на производительность!

8

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)

Это связано с тем, что меньшее количество автомобилей будет соответствовать цене, чем выбор цвета, что дает механизму запросов гораздо меньше данных для анализа.

Известно, что у меня есть два очень похожих индекса, различающихся только порядком полей для ускорения запросов (имя, фамилия) в одном и (фамилия, имя) в другом.


6

Недавно я усвоил трюк: 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


5

Предполагая, что здесь MySQL, используйте EXPLAIN, чтобы узнать, что происходит с запросом, убедитесь, что индексы используются максимально эффективно, и попытайтесь исключить сортировку файлов. Высокая производительность MySQL: оптимизация, резервное копирование, репликация и многое другое - отличная книга по этой теме, как и блог о производительности MySQL .


3
Это хорошо для MySQL, но вопрос был помечен как «sqlserver». Тем не менее, это хорошо. Аналогичным образом в SSMS можно использовать «Показать предполагаемый план выполнения» и «Включить фактический план выполнения». Если вы можете отказаться от сканирования огромных таблиц и использовать поиск по кластерному индексу, то вы на пути к оптимальной производительности.
eksortso

5

@Terrapin есть еще несколько различий между isnull и coalesce, которые стоит упомянуть (помимо соответствия ANSI, что для меня очень важно).

Coalesce против IsNull


3

Иногда в SQL Server, если вы используете OR в предложении where, это действительно снижает производительность. Вместо использования ИЛИ просто сделайте два выбора и объедините их вместе. Вы получаете те же результаты на 1000-кратной скорости.


Я видел это необъяснимое поведение.
Esen

2

Посмотрите на предложение where - проверьте использование индексов / убедитесь, что ничего глупого не делается

where SomeComplicatedFunctionOf(table.Column) = @param --silly

2

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


2

Во все мои временные таблицы я люблю добавлять уникальные ограничения (где это необходимо) для создания индексов и первичные ключи (почти всегда).

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)

2

Я взял за привычку всегда использовать переменные связывания. Возможно, переменные связывания не помогут, если СУБД не кэширует операторы SQL. Но если вы не используете переменные связывания, у РСУБД нет возможности повторно использовать планы выполнения запросов и проанализированные операторы SQL. Экономия может быть огромной: http://www.akadia.com/services/ora_bind_variables.html . Я работаю в основном с Oracle, но Microsoft SQL Server работает почти так же.

По моему опыту, если вы не знаете, используете ли вы переменные связывания, скорее всего, нет. Если ваш язык приложения не поддерживает их, найдите тот, который поддерживает. Иногда вы можете исправить запрос A, используя переменные связывания для запроса B.

После этого я разговариваю с нашим администратором баз данных, чтобы выяснить, что причиняет РСУБД наибольшую боль. Обратите внимание, что вы не должны спрашивать «Почему этот запрос медленный?» Это все равно, что попросить врача удалить вам аппендикс. Конечно, проблема может быть в вашем запросе, но так же вероятно, что что-то еще не так. Как разработчики, мы склонны мыслить строчками кода. Если линия медленная, исправьте эту линию. Но СУБД - это действительно сложная система, и ваш медленный запрос может быть признаком гораздо более серьезной проблемы.

Слишком много советов по настройке SQL - кумиры культа карго. В большинстве случаев проблема не связана или минимально связана с используемым синтаксисом, поэтому обычно лучше использовать максимально чистый синтаксис. Затем вы можете начать искать способы настройки базы данных (а не запроса). Изменяйте синтаксис только тогда, когда это не удается.

Как и при любой настройке производительности, всегда собирайте значимую статистику. Не используйте время настенных часов, если это не настраивается пользователем. Вместо этого посмотрите на такие вещи, как время ЦП, извлеченные строки и блоки, считанные с диска. Слишком часто люди оптимизируются не для того.


2

Первый шаг: посмотрите на план выполнения запроса!
TableScan -> плохой
NestedLoop -> предупреждение
Meh TableScan за NestedLoop -> DOOM!

УСТАНОВИТЬ СТАТИСТИКУ IO ВКЛ
УСТАНОВИТЬ ВРЕМЯ СТАТИСТИКИ ВКЛ


2

Выполнение запроса с использованием WITH (NoLock) для меня в значительной степени является стандартной операцией. Любой, кто поймал выполнение запросов к таблицам размером в десятки гигабайт, без этого вынимается и расстреливается.


2
Это следует использовать с умом, а не по привычке. Блокировка - это не зло, это просто неправильно.

2

Преобразуйте запросы 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

1

@ DavidM

Предполагая, что здесь MySQL, используйте EXPLAIN, чтобы узнать, что происходит с запросом, убедитесь, что индексы используются максимально эффективно ...

В SQL Server план выполнения дает вам то же самое - он сообщает вам, какие индексы используются и т. Д.


1

Индексируйте таблицы по критериям, которые вы фильтруете


1

Не обязательно трюк с производительностью SQL как таковой, но определенно связанный:

Хорошей идеей было бы использовать memcached там, где это возможно, поскольку это было бы намного быстрее, просто извлекая предварительно скомпилированные данные непосредственно из памяти, а не из базы данных. Также есть разновидность MySQL, в которую встроен memcached (сторонний).


1

Убедитесь, что длина вашего индекса как можно меньше. Это позволяет БД читать больше ключей из файловой системы за раз, тем самым ускоряя ваши соединения. Я предполагаю, что это работает со всеми БД, но я знаю, что это конкретная рекомендация для MySQL.


1

Я ищу:

  • Разверните все циклы CURSOR и преобразуйте их в операторы UPDATE / INSERT на основе набора.
  • Обратите внимание на любой код приложения, который:
    • Вызывает SP, который возвращает большой набор записей,
    • Затем в приложении просматривает каждую запись и вызывает SP с параметрами для обновления записей.
    • Преобразуйте это в SP, который выполняет всю работу за одну транзакцию.
  • Любой SP, который выполняет много операций со строками. Это свидетельство того, что данные неправильно структурированы / нормализованы.
  • Любой SP, который заново изобретает колесо.
  • Любой SP, который я не могу понять, что он пытается сделать в течение минуты!

1
SET NOCOUNT ON

Обычно первая строка внутри моих хранимых процедур, если мне действительно не нужно использовать @@ROWCOUNT.


2
@@ ROWCOUNT все равно устанавливается. NOCOUNT отключает операторы «затронутых строк xx».
Sklivvz 01

Действительно ли это когда-либо оказывает заметное влияние на производительность?
JohnFx

Да, тогда счетчик не вычисляется автоматически каждый раз при запуске оператора SQL. Достаточно легко протестировать запрос с учетом и без него, чтобы увидеть, насколько он важен.
Travis

В любом случае счетчик отслеживается в SQL Server. Любая разница в производительности, которую вы видите, связана с тем, что счетчики должны пройти по сети на ваш интерфейс. Если вы делаете один SELECT, это не будет иметь заметного значения. Если у вас есть петля со 100000 вставками, это намного больше по сети.
Tom H

1

В SQL Server используйте директиву nolock. Это позволяет команде select завершиться без ожидания - обычно заканчиваются другие транзакции.

SELECT * FROM Orders (nolock) where UserName = 'momma'

3
NOLOCK предназначен только для запросов, правильные результаты которых вас не интересуют,
Марк Соул

1

Удалите курсоры везде, где они не нужны.


Ага, курсоры - это проклятие! ;)
Sklivvz 01

8
Тьфу. Не выбрасывайте это так безоговорочно. Курсоры похожи на пистолеты. Сами по себе они неплохие, просто люди делают с ними действительно плохие вещи.
JohnFx

1

Удалите вызовы функций в Sprocs, где многие строки будут вызывать функцию.

Мой коллега использовал вызовы функций (например, получение lastlogindate из идентификатора пользователя) для возврата очень широких наборов записей.

Для оптимизации я заменил вызовы функций в sproc на код функции: у меня время работы многих sproc уменьшилось с> 20 секунд до <1.


0
  • Для всех таблиц используйте префикс dbo. чтобы предотвратить перекомпиляцию.
  • Просматривайте планы запросов и ищите сканирование таблиц / индексов.
  • В 2005 году проверьте представления управления на предмет отсутствующих индексов.


0

Не ставьте перед именами хранимых процедур префикс sp_, потому что все системные процедуры начинаются с sp_, и SQL Server будет труднее искать вашу процедуру при ее вызове.


1
Вы действительно тестировали это? Если SQL Server делает то, что разумно (используя алгоритм хеширования для поиска хранимой процедуры), это не имеет значения. Фактически, если бы SQL Server этого не делал, похоже, что производительность системы была бы вонючей (поскольку он предположительно вызывает свои собственные процедуры).
Джон Стауффер,

1
Я думаю, это попадает в корзину преждевременной оптимизации. Вероятно, это хорошая практика, чтобы не
вводить

0

Грязные чтения -

set transaction isolation level read uncommitted

Предотвращает мертвые блокировки, когда целостность транзакции не является абсолютно необходимой (что обычно верно)


1
Да, но это может привести к странным ошибкам, которые ОЧЕНЬ сложно найти.
Грант Джонсон,

0

Я всегда сначала использую SQL Profiler (если это хранимая процедура с большим количеством уровней вложенности) или планировщик выполнения запросов (если это несколько операторов SQL без вложенности). В 90% случаев вы можете сразу найти проблему с помощью одного из этих двух инструментов.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.