Избранные трюки настройки производительности

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

Ответ 1

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

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

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

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

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

Ответ 2

  • Имейте довольно хорошее представление об оптимальном пути выполнения запроса в вашей голове.
  • Проверить план запроса - всегда.
  • Включите STATS, чтобы вы могли анализировать производительность IO и CPU. Сосредоточьтесь на том, чтобы свести эти цифры вниз, не обязательно время запроса (на что может повлиять другая активность, кеш и т.д.).
  • Посмотрите на большое количество строк, входящих в оператор, но небольшие числа выходят. Как правило, индекс помогает ограничить количество входящих строк (что экономит чтение диска).
  • Сначала сосредоточьтесь на самом высоком поддереве стоимости. Изменение этого поддерева часто может изменить весь план запроса.
  • Общие проблемы, которые я видел:
    • Если есть много подключений, иногда Sql Server будет выбирать расширение соединений, а затем применять предложения WHERE. Обычно вы можете исправить это, переместив условия WHERE в предложение JOIN или производную таблицу с установленными условиями. Представления могут вызывать те же проблемы.
    • Субоптимальные объединения (LOOP vs HASH vs MERGE). Мое правило состоит в том, чтобы использовать объединение LOOP, когда верхняя строка имеет очень мало строк по сравнению с нижней, MERGE, когда наборы примерно равны и упорядочены, а HASH - для всего остального. Добавление подсказки соединения позволит вам проверить вашу теорию.
    • Параметр sniffing. Если вы сначала запустили хранимую процедуру с нереалистичными значениями (скажем, для тестирования), тогда план кэшированных запросов может быть субоптимальным для ваших производственных значений. Запуск снова с помощью RECOMPILE должен проверить это. Для некоторых хранимых процедур, особенно тех, которые имеют дело с диапазонами различного размера (скажем, всех дат между сегодняшним днем ​​и вчера), что повлечет за собой ИНДЕКС SEEK - или все даты между прошлым годом и этим годом - что было бы лучше с помощью INDEX SCAN), возможно, вам придется запускать его с RECOMPILE каждый раз.
    • Плохой отступ... Хорошо, поэтому у Sql Server нет проблемы с этим, но я уверен, что понять его невозможно, пока я не исправил форматирование.

Ответ 3

Слегка от темы, но если у вас есть контроль над этими проблемами...
Высокий уровень и высокое воздействие.

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

Ответ 4

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)

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

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

Ответ 5

Недавно я узнал, что 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

Ответ 6

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

Ответ 7

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

Coalesce vs. IsNull

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

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

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

Ответ 12

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

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

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

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

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

Ответ 13

Первый шаг: Посмотрите на план выполнения запросов!
TableScan → плохо
NestedLoop → meh warning
TableScan за NestedLoop → DOOM!

НАСТРОЙКА СТАТИСТИКИ IO ON
SET STATISTICS TIME ON

Ответ 14

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

Ответ 15

Преобразуйте NOT IN запросы в LEFT OUTER JOINS, если это возможно. Например, если вы хотите найти все строки в таблице 1, которые не используются внешним ключом в таблице 2, вы можете сделать это:

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

Ответ 16

@DavidM

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

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

Ответ 17

Индексируйте таблицу (таблицы) с помощью clm (s), которые вы фильтруете с помощью

Ответ 18

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

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

Ответ 19

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

Ответ 20

Я смотрю:

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

Ответ 21

SET NOCOUNT ON

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

Ответ 22

Удалить курсоры везде, где они не являются ключевыми.

Ответ 23

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

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

Ответ 24

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

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

Задав оптимизацию, я заменил вызовы функций в sproc кодом функции: у меня было много времени работы sprocs s > 20 секунд до < 1.

Ответ 25

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

Ответ 26

Мне нравится использовать

isnull(SomeColThatMayBeNull, '')

Более

coalesce(SomeColThatMayBeNull, '')

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

http://blog.falafel.com/2006/04/05/SQLServerArcanaISNULLVsCOALESCE.aspx

Ответ 27

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

Ответ 28

Dirty читает -

set transaction isolation level read uncommitted

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

Ответ 29

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