Предыдущая статья: Без новых индексов или изменений схемы - запросы быстрее в 10 раз с Batch Mode для Rowstore (Часть 4)
Давайте пристально изучим Actual Execution Plan, и углубимся в изучение пакетного режима для строчного хранения.
Предыдущая статья: Без новых индексов или изменений схемы - запросы быстрее в 10 раз с Batch Mode для Rowstore (Часть 4)
Давайте пристально изучим Actual Execution Plan, и углубимся в изучение пакетного режима для строчного хранения.
Надеюсь, вам нравится эта серия. Я искренне верю, что она может быть очень полезной для тех, кто профессионально использует SQL Server, как это делаю я уже более 20 лет (как летит время!).
Сегодня мы поговорим о пакетном режиме для строчного хранения (Batch Mode on Rowstore) и о том, как сделать ваш запрос в 10 раз быстрее без добавления каких-либо индексов.
Что ж... в Части 3 мы представили колоночные индексы и увидели, как выполнение в пакетном режиме может кардинально улучшить аналитические запросы.
Если вы пропустили предыдущую часть, вы можете прочитать её здесь:
Но вот в чём поворот.
Начиная с SQL Server 2019, вам не всегда нужен колоночный индекс, чтобы получить производительность пакетного режима.
SQL Server может активировать пакетный режим для строчного хранения.
И иногда... как я сказал вам в начале этой статьи... ваш запрос становится в 5 или 10 раз быстрее без добавления ни одного индекса. Поэтому я предлагаю вам продолжить чтение...
Иногда я действительно ненавижу свою работу.
Вечно, ВЕЧНО, я мог утверждать: «Когда вы измеряете производительность хранилища во время настройки индексов и запросов, вы всегда должны использовать логические чтения, а не физические, потому что логические чтения повторяемы, а физические — нет. Физические чтения могут меняться в зависимости от того, что находится в кэше, какие другие запросы выполняются в данный момент, от редакции вашего SQL Server и от того, используются ли упреждающие чтения. Логические чтения просто отражают точное количество прочитанных страниц, независимо от того, откуда пришли данные (с диска или из кэша), так что пока это число уменьшается, вы делаете свою работу хорошо».
В предыдущих статьях мы сравнили фильтрованные индексы и индексированные представления, поняв, в каких случаях каждый из них проявляет себя наилучшим образом.
Если вы пропустили Часть 2, вы можете прочитать её здесь:
👉 Фильтрованные индексы: сравнение производительности с примерами (Часть 2)
Сегодня мы добавляем в игру нового игрока.
Потому что, когда объём данных начинает расти… когда миллионы строк превращаются в десятки или сотни миллионов… мы получаем мощного нового союзника:
Колоночные индексы (Columnstore Indexes).
И это меняет всё.
Эта статья является продолжением предыдущего глубокого погружения в настройку производительности SQL Server!
Если вы пропустили Часть 1, вы можете прочитать её здесь:
Как фильтрованные индексы кардинально улучшают производительность запросов (часть 1)
В первой статье мы проанализировали, как фильтрованные индексы могут кардинально сократить логические чтения и оптимизировать планы выполнения.
Сегодня мы углубляемся и сравниваем фильтрованный индекс с индексированным представлением, используя практические, воспроизводимые SQL-скрипты.
Вы найдёте конкретные сценарии, которые сможете протестировать в своей собственной лаборатории — потому что настройка производительности — это не теория, это экспериментирование.
Сегодня мы погружаемся в мощную технику настройки производительности SQL Server, которая может кардинально сократить логические чтения, оптимизировать планы выполнения и значительно улучшить производительность запросов в реальных производственных средах.
Если вы работаете с Microsoft SQL Server и боретесь с медленными запросами, высокими логическими чтениями или неэффективными планами выполнения, эта продвинутая техника настройки производительности SQL Server может кардинально улучшить производительность запросов.
В этой статье мы проанализируем, как фильтрованные индексы в SQL Server могут уменьшить ввод-вывод, оптимизировать планы выполнения и значительно повысить производительность OLTP.
Мы часто выполняем плановые переключения реплик в группах доступности SQL Server для обслуживания, установки исправлений, обновлений и даже ротации оборудования. Обычно наши переключения выполняются быстро, но иногда они занимают больше времени — и не всегда интуитивно понятно почему, поскольку нет очевидной связи со временем суток, размером базы данных или объёмом транзакций.
Сокращение даже нескольких секунд из этого процесса может улучшить взаимодействие с приложением и конечным пользователем; это также может значительно снизить количество оповещений или, по крайней мере, сократить время, в течение которого оповещения должны быть отключены. Существует множество материалов о том, как правильно выполнять переключения в AG (без потери данных), но гораздо меньше тех, которые сосредоточены на сокращении окна прерывания доступности. Разница обычно заключается в некоторой комбинации объёма повторного выполнения (redo), поведения контрольных точек, открытых транзакций и готовности вторичной реплики.
Я хотел поделиться некоторыми методами, которые я использую, чтобы сделать плановые переключения более быстрыми и предсказуемыми. Некоторые из этих методов хорошо документированы, другие выпестованы из реальных шаблонов, которые я наблюдал во многих средах с SQL Server. Я расскажу о том, что я делаю до, во время и после переключения первичной реплики, чтобы минимизировать прерывания работы пользователей и повысить вероятность того, что они не заметят, что что-то произошло.
Это случай, произошедший в прошлом году в системе одного клиента: иногда обычный запрос к крошечной таблице, казалось, «зависал», и его приходилось убивать и запускать заново. В чём же дело?
Рассматриваемая таблица содержала всего несколько миллионов строк данных с максимальным размером строки 60 байт, и запрос обычно выполнялся за несколько секунд, но иногда он «зависал» и либо его приходилось убивать, либо он выполнялся десятки минут. Диагностические средства, запущенные во время возникновения проблемы, не показывали каких-либо необычных ожиданий, давления на сервер не было, а план запроса, генерируемый при долгом выполнении, был практически таким же.
SQL Server предоставляет параметр оптимизации для нерегламентированных рабочих нагрузок (ad-hoc workloads), действующий в рамках всего сервера, который используется для уменьшения объёма памяти, занимаемого одиночными ad-hoc пакетами и связанными с ними планами. Когда этот параметр включён на уровне экземпляра SQL Server, при первом выполнении пакета ad-hoc для любой базы данных на экземпляре сохраняется «заглушка» скомпилированного плана с уменьшенным потреблением памяти. Эта опция сервера OPTIMIZE_FOR_AD_HOC_WORKLOADS доступна начиная с SQL Server 2019, и имет область действия на уровне базы данных.
Я работал над запросом в службу поддержки, в котором наш клиент обнаружил большое количество планов выполнения, потребляющих ресурсы в кэше планов для одного запроса. Я хотел бы поделиться своими выводами и опытом по предотвращению подобных проблем.
"Parameter Sniffing" (конфиденциальность параметров) в SQL Server происходит, когда план выполнения запроса генерируется с использованием конкретных значений параметров. Последующие выполнения того же запроса могут работать плохо с другими значениями параметров из-за неподходящего кэшированного плана. Вот как можно решить эту проблему:

Автор: Paul White, SQL Server Forced Plans Overwite the Query Hash
Если вы «принудительно» задаёте план, любым методом, включая руководство планом (Plan Guides), хранилище запросов (Query Store) и автоматическое исправление плана (Automatic Plan Correction), результирующий план будет иметь свой query_hash перезаписанным значением query_plan_hash.
Другими словами, хэш плана и хэш плана запроса получат значение хэша плана запроса. Реальное значение хэша запроса просто теряется. 🤦
Это нарушит работу всего, где вы используете query_hash для любых целей, включая скрипты и инструменты.
Обычно, когда SQL Server обновляет статистику по объекту, он также аннулирует кешированные планы выполнения, которые полагаются на эту статистику. Именно поэтому вы видите повторную компиляцию после обновления статистики: SQL Server знает, что статистика изменилась, и это хороший момент для построения новых планов выполнения с учётом изменений в данных.
Однако обновление статистики, созданной системой, не обязательно приводит к повторной компиляции планов.
Это действительно странный крайний случай, и вы, вероятно, никогда с ним не столкнётесь, но я сталкиваюсь с ним на каждом занятии, которое провожу. Я каждый раз мимоходом упоминаю об этом в классе и даже не обращаю на это особого внимания. Однако недавно студент спросил меня: «Это где-нибудь задокументировано?», и я подумал, э-э, может быть, но я не уверен, так что лучше задокументировать это здесь, в старом добром блоге.
На Stack Overflow у нас есть несколько таблиц с кластерными columnstore-индексами, которые отлично работают для большей части нашей нагрузки. Но мы недавно столкнулись с ситуацией, когда «идеальные штормы» — несколько процессов, пытающихся одновременно удалить данные из одного columnstore-индекса — перегружали процессор, поскольку они все запускались с высокой степенью параллелизма и боролись за завершение своей операции.
Мы недавно обновили несколько систем до SQL Server 2025. Само обновление ядра прошло гладко, но в наших контурах предварительной подготовки, когда мы планировали переход в прод, возникли три неожиданные проблемы. Ни одна из них не помешала завершению обновления, но все три могли легко сорвать в остальном плавное обновление на месте до SQL Server 2025. Что это были за проблемы и как можно избежать их возникновения?
SQL Server 2025 представляет новую возможность Resource Governor для управления использованием tempdb, а также делает Resource Governor доступным в Standard Edition.
Мне стало интересно: может ли новая функция tempdb в Resource Governor помочь сдержать запросы, которые не используют временные таблицы, но вызывают массовое переполнение данных в tempdb? В документации говорится «да», но я всегда предпочитаю получить практический опыт, когда это возможно.
У меня есть ужасный запрос, который «переливается через край», как автомат с мягким мороженым, объявивший войну. Давайте протестируем новые функции управления tempdb в SQL Server 2025.
Статистики в SQL Server теоретически просты: они помогают оптимизатору оценить, сколько строк может вернуть запрос.
На практике? Всё быстро становится странным. Особенно когда вы начинаете фильтровать по нескольким столбцам или задаётесь вопросом, почему оптимизатор думает, что вернутся миллионы строк, когда вы знаете, что их всего несколько сотен тысяч.
В этой статье я на примерах разберу одноколоночные, многоколоночные и фильтрованные статистики — покажу, в каких случаях оценки не соответствуют действительности, когда они приходят в норму и почему это не всегда означает, что нужно обновить всё с помощью FULLSCAN.
Дата релиза SQL Server 2025: 18 ноября 2025 г.
В этой статье собран подробный обзор нововведений Always On в SQL Server 2025: быстрый фейловер при устойчивых проблемах, ускорение синхронизации при переключениях, улучшения отказоустойчивости после кратковременной потери кворума, усиление безопасности соединений (TLS 1.3 и TDS 8.0), гибкость управления прослушивателями и маршрутизацией трафика, а также поддержка полноценных резервных копий на вторичных репликах. Материал ориентирован на проектирование, внедрение и эксплуатацию HA/DR решений. Подготовлено с помощью GPT5.
Распределение памяти (memory grants) под запросы — один из ключевых и при этом часто недооцениваемых аспектов настройки производительности SQL Server. Если запрос просит больше памяти, чем ему нужно, падает параллелизм. Если слишком мало — происходят проливы в tempdb.
В SQL Server 2025 Microsoft улучшила обратную связь по распределению памяти запроса (query memory grant feedback) и оптимизацию планов выполнения, благодаря чему движок управляет памятью заметно эффективнее, чем в SQL Server 2022. В этой статье мы рассмотрим, как эти изменения влияют на производительность запросов, сравним уровни совместимости 160 и 170.
В первой части статьи «Малоиспользуемые индексы в группах доступности – Часть 1» я показал, как с помощью динамического SQL собрать статистику использования индексов для заданной таблицы со всех реплик в группе доступности. Знать использование по всем нагрузкам, безусловно, лучше, чем смотреть только на первичную или на одну вторичную. Но что если я хочу принимать ещё более взвешенные решения, добавив к выводу количество строк, размер и столбцы индексов?