Каковы наилучшие методы оптимизации производительности SQL Server?

Я всегда использовал подход к развертыванию базы данных с минимальным набором индексов, а затем добавление/изменение индексов по мере того, как производительность диктует.

Этот подход работает достаточно хорошо. Тем не менее, он все еще не говорит мне, где я мог бы улучшить производительность. Это только говорит мне, где производительность настолько плоха, что пользователи жалуются на это.

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

Итак, не стоит ли искать улучшения производительности, поскольку "преждевременная оптимизация - это корень всего зла"?

При рефакторинге кода приложения разработчик постоянно ищет способы улучшить качество кода. Есть ли способ постоянно искать улучшения производительности базы данных? Если да, то какие инструменты и методы вы считаете наиболее полезными?

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

Ответ 1

Мой подход заключается в сборе команд против сервера или базы данных в таблицу с использованием SQL Server Profiler. После этого вы можете запросить на основе времени выполнения max и avg, max и avg cpu, а также (также очень важно) количества раз, когда запрос выполнялся.

Так как я пытаюсь поместить весь код доступа к базе данных в хранимые процедуры, мне легко выполнить запросы. Если вы используете встроенный SQL, это может быть сложнее, поскольку изменение значения в запросе сделает его похожим на другой запрос. Вы можете попытаться обойти это, используя оператор LIKE, для ввода одинаковых типов запросов в одни и те же ведра для вычисления агрегатов (max, avg, count).

После того, как у вас есть список "лучших 10" потенциальных проблем, вы можете начать смотреть на них индивидуально, чтобы увидеть, можно ли переработать запрос, индекс может помочь или сделать небольшое изменение архитектуры в порядке. Чтобы придумать топ-10, попробуйте просмотреть данные по-разному: avg * count для общей стоимости в течение периода, max для худшего преступника, просто avg и т.д.

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

Удачи!

Ответ 2

"преждевременная оптимизация - корень всего зла"

Что касается программирования баз данных, я думаю, что эта цитата - глупость. Очень дорого переписать все ваше приложение, потому что ваши разработчики не хотят писать эффективный код в первый раз. Весь код t-sql следует учитывать с точки зрения того, как это повлияет на производительность базы данных во-вторых (целостность данных, конечно, первая). Perfomance должен превзойти все, кроме целостности данных.

Да, есть вещи оптимизации, которые вы не должны делать до тех пор, пока у вас не возникнут проблемы, но некоторые вещи должны быть выполнены, как само собой разумеющееся, а не исправлены позже. Не требуется больше времени для написания кода, который имеет больше шансов быть эффективным, чем код, который не будет, когда вы поймете, как вы влияете на эффективность с помощью плохого кода. Одним из примеров является обсуждение кода курсора Cervo. Действия, основанные на настройках, почти всегда намного быстрее, чем курсорные решения, поэтому курсоры никогда не должны записываться первоначально, когда будет установлено решение на основе набора. Это почти всегда занимает меньше времени, чтобы написать решение на основе набора, которое было бы для написания курсора, но единственный способ добиться этого - никогда не писать курсоры.

И нет причины использовать select * вместо указания имен полей. В MSSQL вы можете перетащить эти имена из объектного проводника, чтобы вы не могли сказать мне, что это слишком сложно сделать. Но, указав только нужные вам поля, вы сохраняете сетевые ресурсы и ресурсы сервера баз данных и ресурсы веб-сервера. Так зачем программисту когда-либо брать ленивый вариант select * и беспокоиться об оптимизации позже?

То же самое с индексами. Вы говорите, что вы делаете минимальный набор индексов. В зависимости от того, как вы определяете минимальное, это может быть нормально, но крайне важно иметь индексы по всем внешним ключам, и я бы не хотел подталкивать базу данных, у которой не было индексов на нескольких полях, которые чаще всего встречаются там, где статьи. Если ваши пользователи находятся за пределами клиентов, а не изнутри, они не будут жаловаться на то, как медленно ваш сайт, они отправятся в другое место. Это только делает разумность планирования для эффективного доступа к базе данных с самого начала.

Одна из моих главных забот о том, что с самого начала не считаться с эффективностью, заключается в том, что первые пару раз, когда что-то слишком медленно, компании склонны просто бросать больше оборудования в проблему, а не в настройку производительности. К тому времени, когда люди начинают выполнять настройку, у вас есть несколько гигабайт или больше базы данных со многими несчастными клиентами, которые получают таймауты больше, чем результаты. На данный момент, часто почти все в базе данных должно быть переписано, и тем временем вы теряете клиентов. Я помню, что в одной компании поддерживал коммерческое приложение, которое буквально занимало десять минут, чтобы представители службы поддержки переходили с одного экрана на другой, когда они пытались помочь уже недовольным клиентам по телефону. Вы можете себе представить, сколько клиентов потеряло компания из-за плохо спроектированных запросов к базе данных в коммерческом продукте, которые мы не могли изменить.

Ответ 4

профилируйте ваши запросы, а не очевидные, но сложный, который обращается к различным таблицам, представлениям и т.д. и/или к тем, которые возвращают много строк из разных таблиц.

Это точно скажет вам, где вы должны сосредоточиться

Ответ 5

профилирование является ключевым, но при использовании набора профилирования вы ДОЛЖНЫ быть уверены, что это точный тестовый набор данных, иначе инструменты настройки не смогут получить точный результат, что необходимо.

Также очень полезны также объекты управления с фрагментацией, отчет об использовании в 2005 году.

Ответ 6

После того, как вы профилируете, поместите запросы, которые вы видите как вызывающие проблемы в SQL Query Analyzer, и покажите план выполнения. Определите части запросов, которые выполняют дорогостоящие сканирование таблиц и переиндексируют эти таблицы, чтобы свести к минимуму эту стоимость.

Попробуйте эти ссылки:

Оптимизация SQL
Как оптимизировать запросы

Ответ 7

Конечно, вы должны прокомментировать ваши запросы и посмотреть план выполнения. Но две основные вещи, возникающие снова и снова, отфильтровываются как можно больше, и вы можете избежать курсоров.

Я видел приложение, в котором кто-то загружал всю таблицу событий базы данных в клиенту, а затем проходил через каждую строку по одной фильтрации, основываясь на некоторых критериях. Было усиленное увеличение производительности при передаче критериев фильтрации в базу данных и запрос запроса применял критерии в предложении where. Это очевидно для людей, которые работают с базами данных, но я видел похожие вещи. Также у некоторых людей есть запросы, в которых хранится множество временных таблиц, заполненных строками, которые им не нужны, которые затем удаляются в окончательном соединении временных таблиц. В принципе, если вы исключаете из запросов, которые заполняют временные таблицы, то для остальной части запроса меньше данных, и весь запрос выполняется быстрее.

Курсоры очевидны. Если у вас миллион строк и идти по строкам, то это займет много времени. Выполняя некоторые тесты, если вы подключаетесь к базе данных даже с "медленным" динамическим языком, таким как Perl, и выполняете некоторые операции по строке в наборе данных, скорость будет по-прежнему намного больше, чем курсор в базе данных. Сделайте это с чем-то вроде Java/C/С++, а разница в скорости еще больше. Если вы можете найти/устранить курсор в коде базы данных, он будет работать намного быстрее... Если вы должны использовать курсор, переписывание этой части на любом языке программирования и извлечение ее из базы данных, вероятно, приведет к огромному увеличению производительности.

Еще одно примечание к курсорам, остерегайтесь кода, такого как SELECT @col1 = col1, @col2 = col2, @col3 = col3, где id = @currentid в цикле, который проходит через идентификаторы, а затем выполняет инструкции для каждого столбца. В основном это и курсор. Не только это, но и использование реальных курсоров часто быстрее, чем это, особенно статические и forward_only. Если вы можете изменить операцию, которая будет установлена ​​на основе, она будет намного быстрее..... При этом курсоры имеют место для некоторых вещей... но с точки зрения производительности существует штраф за их использование по установленному основанию подходы.

Также остерегайтесь плана выполнения. Иногда он оценивает операции, которые занимают секунды, чтобы быть очень дорогими, и операции, которые занимают минуты, очень дешевы. При просмотре плана выполнения обязательно проверьте все, возможно, вставив в свой код SELECT "В этой области", GETDATE().

Ответ 8

Мой совет заключается в том, что "преждевременная оптимизация - это корень всего зла" в этом контексте - абсурдная глупость.

На мой взгляд, все это касается дизайна - вам нужно думать о concurrency, горячих точках, индексах, масштабировании и шаблонах использования при проектировании вашей схемы данных.

Если вы не знаете, какие индексы вам нужны и как их нужно настраивать с места в карьер без выполнения профилирования, вы уже провалили.

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

Ответ 9

Кажется, вы говорите о MS SQL.

Запустите профилировщик и запишите наиболее распространенные запросы, которые вы запускаете в базе данных. Затем запустите эти запросы с включенным планом выполнения, и вы увидите, что (если что-либо) замедляет ваши запросы. Затем вы можете оптимизировать запросы или добавить дополнительные индексы в свои поля.

Книги SQL предоставят вам хороший обзор функций профилирования и анализа запросов.

Ответ 10

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

Ответ 11

Убедитесь, что вы профилируете с использованием объемов производства - по количеству строк и. Запросы и их планы ведут себя по-разному при разных сценариях загрузки/объема

Ответ 12

Как правило, советы здесь:

http://www.sql-server-performance.com/

были качественными и полезными для меня в прошлом.

Ответ 13

Применить правильную индексацию в столбцах таблицы в базе данных

  • Убедитесь, что в каждой таблице базы данных есть первичный ключ.

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

  • Создайте некластеризованные индексы в столбцах

    Часто используется в критериях поиска.

    Используется для соединения с другими таблицами.

    Используется как поля внешнего ключа.

    Высокая избирательность (столбец, который возвращает низкий процент (0-5%) строк из общего количества строк на определенное значение).

    Используется в предложении ORDER BY.

Не используйте "SELECT *" в SQL-запросе

Необязательные столбцы могут быть извлечены, что добавит затраты на время поиска данных. Механизм базы данных не может использовать преимущество "Covered Index" и, следовательно, запрос выполняется медленно.

Пример:

SELECT Cash, Age, Amount FROM Investments; 

Вместо:

SELECT * FROM Investments;

Попытайтесь избежать предложения HAVING в операторах Select

Предложение HAVING используется для фильтрации строк после выбора всех строк и используется как фильтр. Старайтесь не использовать предложение HAVING для любых других целей.

Пример:

SELECT Name, count (Name) FROM Investments WHERE Name!= ‘Test’ AND Name!= ‘Value’ GROUP BY Name;

Вместо:

SELECT Name, count (Name) FROM Investments GROUP BY Name HAVING Name!= ‘Test’ AND Name!= ‘Value’ ;

Попробуйте минимизировать количество блоков вспомогательных запросов в запросе

Иногда у нас может быть несколько подзапросов в нашем основном запросе. Мы должны попытаться свести к минимуму количество блоков вспомогательных запросов в нашем запросе.

Пример:

SELECT Amount FROM Investments WHERE (Cash, Fixed) = (SELECT MAX (Cash), MAX (Fixed) FROM Retirements) AND Goal = 1; 

Вместо:

SELECT Amount FROM Investments WHERE Cash = (SELECT MAX (Cash) FROM Retirements) AND Fixed = (SELECT MAX (Fixed) FROM Retirements) AND Goal = 1;

Избегайте ненужных столбцов в списке SELECT и ненужных таблиц в условиях соединения

Выбор ненужных столбцов в запросе Select добавляет служебные данные к фактическому запросу, особенно если ненужные столбцы имеют типы LOB. Включение ненужных таблиц в условия соединения заставляет механизм базы данных извлекать и извлекать ненужные данные и увеличивает время выполнения запроса.

Не используйте агрегат COUNT() в подзапросе, чтобы выполнить проверку существования

Когда вы используете COUNT(), SQL Server не знает, что вы делаете проверку существования. Он подсчитывает все соответствующие значения, либо путем сканирования таблицы, либо путем сканирования самого маленького некластеризованного индекса. Когда вы используете EXISTS, SQL Server знает, что вы выполняете проверку существования. Когда он находит первое совпадающее значение, он возвращает TRUE и перестает смотреть.

Попробуйте избежать соединения между двумя типами столбцов

При объединении двух столбцов разных типов данных один из столбцов должен быть преобразован в тип другого. Столбец, тип которого ниже, тот, который преобразован. Если вы присоединяетесь к таблицам с несовместимыми типами, один из них может использовать индекс, но оптимизатор запросов не может выбрать индекс для столбца, который он преобразует.

Попробуйте не использовать COUNT (*) для получения количества записей в таблице

Чтобы получить общее количество строк в таблице, мы обычно используем следующий оператор Select:

SELECT COUNT(*) FROM [dbo].[PercentageForGoal]

Этот запрос выполнит полное сканирование таблицы, чтобы получить количество строк. Следующий запрос не требует полного сканирования таблицы. (Обратите внимание, что это может не дать вам 100% отличных результатов всегда, но это удобно, только если вам не нужен идеальный счет.)

SELECT rows FROM sysindexes
WHERE id = OBJECT_ID('[dbo].[PercentageForGoal]') AND indid< 2

Попробуйте использовать операторы типа EXISTS, IN и JOINS соответственно в вашем запросе

  • Обычно IN имеет наименьшую производительность.
  • IN эффективен, только когда большинство критериев фильтра для выбора помещаются в подзапрос инструкции SQL.
  • EXISTS эффективен, когда большинство критериев фильтра для выбора находится в основном запросе оператора SQL.

Попробуйте избежать динамического SQL

Если это действительно необходимо, старайтесь избегать использования динамического SQL, потому что: Динамический SQL трудно отлаживать и устранять неполадки. Если пользователь предоставляет вход для динамического SQL, тогда есть вероятность атаки SQL-инъекций.

Старайтесь избегать использования временных таблиц

Если это действительно необходимо, старайтесь избегать использования временных таблиц. Скорее используйте переменные таблицы. В 99% случаев переменные таблицы хранятся в памяти, следовательно, это намного быстрее. Временные таблицы находятся в базе данных TempDb. Таким образом, работа на временных таблицах требует взаимодействия между базами данных и, следовательно, будет медленнее.

Вместо поиска LIKE используйте полнотекстовый поиск для поиска текстовых данных

Полнотекстовые поисковые запросы всегда превосходят LIKE-запросы. Полнотекстовый поиск позволит вам реализовать сложные критерии поиска, которые не могут быть реализованы с помощью поиска LIKE, например, поиск по одному слову или фразе (и, возможно, ранжирование набора результатов), поиск по слову или фразе, близкой к другой слово или фразу, или поиск синонимов формы определенного слова. Реализация полнотекстового поиска проще реализовать, чем поиск LIKE (особенно в случае сложных требований к поиску).

Попробуйте использовать UNION для реализации операции "ИЛИ"

Не пытайтесь использовать "ИЛИ" в запросе. Вместо этого используйте "UNION", чтобы объединить результирующий набор из двух выделенных запросов. Это улучшит производительность запросов. Лучше использовать UNION ALL, если не требуется выдающийся результат. UNION ALL быстрее, чем UNION, поскольку ему не нужно сортировать результирующий набор, чтобы узнать отличительные значения.

Реализовать ленивую стратегию загрузки для больших объектов

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

Внедрить следующие рекомендации в пользовательских функциях

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

Внедрить следующие рекомендации в триггерах

  • Старайтесь избегать использования триггеров. Запуск триггера и выполнение инициирующего события - дорогостоящий процесс.
  • Никогда не используйте триггеры, которые могут быть реализованы с использованием ограничений.
  • Не используйте один и тот же триггер для различных событий запуска (Insert, Update и Delete).
  • Не используйте транзакционный код внутри триггера. Триггер всегда работает внутри транзакционной области кода, запускающего триггер.

Ответ 14

Моим советом было бы начать с методов, применимых ко всем базам данных, а затем попробовать те, которые характерны для MsSQL.

Оптимизация SQL затруднена, и нет жестких и быстрых правил. Вы можете следовать нескольким общим рекомендациям, например:

  • 95% производительности улучшится от приложения, а не от конфигурации сервера или базы данных.
  • Сначала создайте правильность, настройте более высокую производительность
  • Сократить поездки в базу данных
  • Попробуйте выразить вещи так, чтобы они соответствовали вашей модели данных.
  • Игнорировать общий совет о производительности - да, в какой-то момент вы найдете системную или SQL-инструкцию, где одно из этих правил не применяется.

Но главное, что вы должны всегда применять правило 80-20. Это означает, что в любой системе вам нужно настроить 20% (часто намного меньше) вашего кода для достижения максимальной производительности. То, что поставщик предоставил инструменты обычно не работает, поскольку они обычно не могут угадать прикладной/бизнес-контекст исполнения.