Я собираюсь ответить на ваши вопросы в другом порядке, чем вы их задавали.
4. Является ли это симптомом более серьезной проблемы?
Новый оценщик мощность в SQL Server 2016 может быть причиной проблемы. SQL Server 2012 использует устаревшую версию CE, и у вас не возникало проблем с этой версией. Новый оценщик кардинальности делает различные предположения о ваших данных и может генерировать разные планы запросов для одного и того же SQL. У вас может быть более высокая производительность для некоторых запросов с устаревшим CE в зависимости от вашего запроса и ваших данных. Таким образом, некоторые части вашей модели данных могут не подходить для нового CE. Это нормально, но вам, возможно, придется поработать над новым CE на данный момент.
Я также был бы обеспокоен непостоянной производительностью запросов даже при ежедневных обновлениях статистики. Важно отметить, что сбор статистики по всем таблицам будет эффективно уничтожать все планы запросов из кэша, поэтому у вас могут возникнуть проблемы со статистикой или это может быть связано с анализом параметров. Трудно сделать определение без большого количества информации о вашей модели данных, скорости изменения данных, политиках обновления статистики, о том, как вы вызываете свой код, и т. Д. SQL Server 2016 действительно предлагает некоторые настройки уровня базы данных для поиска параметров, которые могут быть полезны , но это может повлиять на все ваше приложение, а не только на один проблемный запрос.
Я приведу пример сценария, который может привести к такому поведению. Вы сказали:
У некоторых пользователей может быть 1 запись разрешения, у некоторых - до 20 КБ.
Предположим, вы собрали статистику по всем таблицам, которая уничтожает все планы запросов. В зависимости от факторов, упомянутых выше, если первый запрос дня направлен на пользователя с только 1 записью разрешения, то SQL Server может кэшировать план, который хорошо работает для пользователей с 1 записью, но ужасно работает с пользователями с записями по 20 тыс. Если первый запрос дня направлен против пользователя с записями в 20 тысяч, то вы можете получить хороший план для записей в 20 тысяч. Когда код запускается для пользователя с 1 записью, это может быть не самый оптимальный запрос, но он все равно может завершиться в мс. Это действительно похоже на анализ параметров. Это объясняет, почему вы не всегда видите проблему или почему иногда требуются часы, чтобы появиться.
1. Сможет ли новый индекс решить проблему, чтобы никогда больше не выбирать плохой план?
Я думаю, что один из добавленных вами индексов предотвратит проблему, поскольку доступ к требуемым данным через индекс будет дешевле, чем сканирование кластерного индекса по таблице, особенно если сканирование не может быть завершено досрочно. Давайте увеличим плохую часть плана запроса:
По оценкам SQL Server , что только одна строка будет возвращена из объединения на [Permission]
и [Project]
. Для каждой строки во внешнем входе будет выполнено сканирование кластерного индекса [Appointment]
. Все строки будут отсканированы из этой таблицы, но только те, которые соответствуют фильтрации, [Start]
будут возвращены оператору соединения. Внутри оператора соединения результаты еще больше сокращаются.
План запроса, описанный выше, может быть в порядке, если на внешний вход соединения действительно отправляется только одна строка. Однако если оценка количества элементов в результате объединения неверна и мы получаем, скажем, 1000 строк, то SQL Server выполнит 1000 сканирований кластеризованного индекса [Appointment]
. Производительность плана запроса очень чувствительна к вопросам оценки.
Самый прямой способ никогда не получить этот план запроса - создать индекс покрытия для [Appointment]
таблицы. Что-то вроде индексации [ProjectId]
и [Start]
должно делать это. Похоже, это именно тот [idx_appointment_start]
индекс, который вы создали для решения проблемы. Еще один способ отговорить SQL-сервер от выбора плана запроса - это зафиксировать оценку количества элементов в результате объединения [Permission]
и [Project]
. Типичные способы сделать это включают изменение кода, обновление статистики, использование устаревшего CE, создание многостолбцовой статистики, предоставление SQL Server дополнительной информации о локальных переменных, таких как RECOMPILE
подсказка, или материализацию этих строк во временную таблицу. Многие из этих методов не подходят, когда вам нужно время отклика на уровне мс или вам нужно написать код через ORM.
Созданный вами индекс [AppointmentAttendee]
не является прямым способом решения проблемы. Однако вы получите многоколоночную статистику по индексу, и эта статистика может препятствовать неверному плану запросов. Индекс может предоставить более эффективный способ доступа к данным, что также может помешать неверному плану запросов, но я не думаю, что есть какая-либо гарантия того, что это не повторится только при включенном индексе [AppointmentAttendee]
.
3. Как мне убедиться, что это не произойдет с другим запросом / планом?
Я понимаю, почему вы задаете этот вопрос, но он очень широкий. Мой единственный совет - попытаться лучше понять основную причину нестабильности плана запросов, проверить, что у вас есть правильные индексы, созданные для вашей рабочей нагрузки, и тщательно протестировать и контролировать свою рабочую нагрузку. У Microsoft есть несколько общих советов о том, как бороться с регрессиями плана запросов, вызванными новым CE в SQL Server 2016:
Рекомендуемый рабочий процесс для обновления обработчика запросов до последней версии кода:
Обновите базу данных до SQL Server 2016 без изменения уровня совместимости базы данных (оставьте его на прежнем уровне)
Включить хранилище запросов в базе данных. Для получения дополнительной информации о включении и использовании хранилища запросов см. Мониторинг производительности с помощью хранилища запросов.
Подождите достаточно времени, чтобы собрать репрезентативные данные о рабочей нагрузке.
Измените уровень совместимости базы данных на 130
Используя SQL Server Management Studio, оцените, есть ли регрессии производительности по конкретным запросам после изменения уровня совместимости
Для случаев, когда есть регрессии, форсируйте предыдущий план в хранилище запросов.
Если есть планы запросов, которые не выполняются принудительно или производительность по-прежнему недостаточна, рассмотрите возможность возврата уровня совместимости к предыдущему значению и затем обратитесь в службу поддержки клиентов Microsoft.
Я не говорю, что вам нужно перейти на SQL Server 2012 и начать все сначала, но описанная общая методика может оказаться полезной для вас.
2. Должен ли я «форсировать» план, который сейчас работает хорошо?
Это полностью зависит от вас. Если вы считаете, что у вас есть план запроса, который хорошо работает для всех возможных входных параметров, вам не безразличны функциональные возможности хранилища запросов, и вы хотите спокойствия, связанного с форсированием плана запроса, то сделайте это. Форсирование планов запросов, имеющих регрессии, является частью рекомендуемой Microsoft политики обновления до SQL Server 2016.