12.8.26

Лучшая пожарная сигнализация — всё равно пожар


Автор: Pinal Dave, A Better Fire Alarm Is Still a Fire

Многие инциденты с SQL Server, по которым меня вызывают, можно было предотвратить с помощью средств, уже имеющихся в среде заказчика. Вот аудит, который я хотел бы видеть выполненным до того, как прозвучал пейджер, — с полным T-SQL.

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

Документ был точен. Это была неудобная часть. Ни один важный факт не требовал ретроспективного взгляда. Каждое предупреждение существовало до сбоя. Я не разгадал тайну. Я написал отличный отчёт о событии, которого можно было избежать.

Так что эта статья — другой документ, тот, который должен существовать до инцидента. Это восемь проверок, которые я теперь выполняю в каждом проекте, объяснение, почему каждая из них важна, и T-SQL, чтобы увидеть, в каком положении находитесь вы. Первый проход занимает около часа. Тестирование и внедрение любых изменений по-прежнему должны выполняться в рамках обычного контроля изменений.

Краткое примечание. Детали объединены из разных проектов и изменены так, чтобы ничто не идентифицировало клиента. Проверяйте всё, что написано ниже, сначала вне производства, как вы поступили бы с любой информацией из интернета.

Есть простой способ испортить этот аудит до того, как он начнётся: предположить, что любое издание может делать всё. Сначала запишите точную версию и редакцию. Автоматическая настройка (automatic tuning) остаётся функцией Enterprise в коробочном SQL Server. Developer edition до SQL Server 2022 включительно и Enterprise Developer в SQL Server 2025 предоставляют функциональность Enterprise для непроизводственной разработки. Resource Governor доступен в Enterprise и Developer до SQL Server 2022. SQL Server 2025 делает его доступным в Enterprise, Enterprise Developer, Standard и Standard Developer.

SELECT SERVERPROPERTY('ProductVersion')      AS product_version,
       SERVERPROPERTY('ProductMajorVersion') AS major_version,
       SERVERPROPERTY('Edition')             AS edition,
       SERVERPROPERTY('EngineEdition')       AS engine_edition;

Запросы аудита доступны только для чтения, но «только для чтения» не означает «без прав». Требования различаются в зависимости от представления и версии SQL Server. Общие разрешения включают VIEW DATABASE STATE или VIEW DATABASE PERFORMANCE STATE для информации на уровне базы данных и VIEW SERVER STATE или VIEW SERVER PERFORMANCE STATE для информации на уровне экземпляра. Изменение настройки требует отдельных прав и утверждения изменения.

Проверки 1 и 2 выполняются в области базы данных. Детальные запросы статистики и размера таблиц из проверок 5 и 6 также относятся к области базы данных, поэтому выполняйте их в каждой базе данных с поддержкой записи, которую вы намерены проверить. Остальные запросы исследуют общие для экземпляра настройки, tempdb, Resource Governor или все подключённые пользовательские базы данных.

Профилактический проход за один час

Проверка Класс отказа Первые данные для сбора
1 Регрессия плана Состояние автоматической настройки и текущие рекомендации
2 Отсутствие истории производительности Состояние Query Store, ёмкость и свежесть сбора
3 Давление параллельных рабочих процессов Порог стоимости, MAXDOP, ожидания и данные нагрузки
4 Конкуренция за выделение в tempdb Раскладка файлов и устойчивые ожидания страниц выделения
5 Смещение оценки кардинальности Настройки статистики, свежесть, выборка и оценки плана
6 Риск развёртывания больших таблиц Количество строк, пространство, влияние блокировок, влияние на журнал и откат
7 Коллизия рабочей нагрузки Политика Resource Governor и классификация сеансов
8 Медленно развивающиеся условия сбоя Устойчивые блокировки и давление журнала транзакций

Проверка 1: автоматическая коррекция планов с подтверждением

Это та проверка, которая больше всего меня раздражает в поддерживаемых редакциях. Возможность существует с SQL Server 2017, но я до сих пор встречаю системы, где никто даже не проверил её состояние. Включение — это одна команда. Решение о включении — всё ещё изменение, и это не одно и то же.

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

SELECT name, desired_state_desc, actual_state_desc, reason_desc FROM sys.database_automatic_tuning_options;

Если actual_state_desc равно OFF, не переходите сразу к команде ALTER. Прочитайте reason_desc, убедитесь, что версия и редакция поддерживают функцию, и проверьте, что Query Store работоспособен. Одна команда может включить функцию, но она не может создать историю, которая ей нужна.

ALTER DATABASE CURRENT SET AUTOMATIC_TUNING (FORCE_LAST_GOOD_PLAN = ON);

Автоматическая коррекция плана требует, чтобы Query Store был в режиме READ_WRITE, что является проверкой 2. Через неделю запросите у базы данных подтверждения вместо того, чтобы предполагать, что ON означает полезность:

SELECT type, reason, score, execute_action_initiated_by, execute_action_initiated_time, revert_action_initiated_by, revert_action_initiated_time, JSON_VALUE(state, '$.currentValue') AS current_state, JSON_VALUE(state, '$.reason') AS state_reason, JSON_VALUE(details, '$.planForceDetails.queryId') AS query_id, JSON_VALUE(details, '$.planForceDetails.regressedPlanId') AS regressed_plan, JSON_VALUE(details, '$.planForceDetails.recommendedPlanId') AS recommended_plan, JSON_VALUE(details, '$.implementationDetails.script') AS script FROM sys.dm_db_tuning_recommendations ORDER BY score DESC;

Оценка (score) — это расчётная ценность или эффект рекомендации по шкале от 0 до 100, где большие значения считаются лучше. Если вы не готовы включить автоматическую коррекцию, оставьте её выключенной и просматривайте это DMV в течение двух недель. SQL Server всё ещё может выявлять потенциальные регрессии, когда опция отключена. Относитесь к каждой рекомендации как к поводу для расследования, а не как к гарантированному будущему сбою. Это DMV не сохраняется, поэтому перезапуск ядра базы данных очищает его рекомендации. Если история важна, собирайте её в другом месте.

Проверка 2: Query Store включён, но работает ли он?

SQL Server 2022 включает Query Store по умолчанию для вновь созданных баз данных. Базы данных, восстановленные из более ранних версий, и базы данных, перенесённые через обновление на месте, сохраняют предыдущую настройку. В любом случае «включён» не равно «работает».

Отказ, который я нахожу чаще всего, — это Query Store в состоянии READ_ONLY, потому что его хранилище заполнилось месяцы назад. Оно перестало собирать данные бесшумно, никто не был оповещён, и первый, кто замечает это, — тот, кому отчаянно нужна вчерашняя история. Это ужасное время, чтобы обнаружить, что камера наблюдения не записывала.

SELECT actual_state_desc, desired_state_desc, readonly_reason, current_storage_size_mb, max_storage_size_mb, query_capture_mode_desc, size_based_cleanup_mode_desc, stale_query_threshold_days FROM sys.database_query_store_options;

В базе данных с активной рабочей нагрузкой также убедитесь, что Query Store содержит недавние данные о выполнении:

SELECT MAX(last_execution_time) AS latest_captured_execution FROM sys.query_store_runtime_stats;

Значение NULL или неожиданно старая временная метка в загруженной базе данных — повод проверить политику сбора и работоспособность Query Store. Интерпретируйте это с учётом рабочей нагрузки и интервала сбора, потому что простаивающая база данных не должна выдавать свежие выполнения.

Если actual_state_desc и desired_state_desc не совпадают, что-то принудительно перевело его в READ_ONLY, и readonly_reason сообщает, что именно. Это битовая маска. 65536 означает, что превышен размер хранилища — это самый частый случай.

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

ALTER DATABASE CURRENT SET QUERY_STORE ( OPERATION_MODE = READ_WRITE, MAX_STORAGE_SIZE_MB = 2048, QUERY_CAPTURE_MODE = AUTO, SIZE_BASED_CLEANUP_MODE = AUTO, CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 60), MAX_PLANS_PER_QUERY = 200, INTERVAL_LENGTH_MINUTES = 60, DATA_FLUSH_INTERVAL_SECONDS = 900 );

Два замечания важны. QUERY_CAPTURE_MODE = ALL может заполнить хранилище одноразовыми ad hoc-запросами в некоторых рабочих нагрузках. Кроме того, установите оповещение о состоянии. Красная строка в эпизодическом аудите — это не стратегия оповещения:

IF DATABASEPROPERTYEX(DB_NAME(), 'Updateability') = 'READ_WRITE' AND EXISTS (SELECT 1 FROM sys.database_query_store_options WHERE actual_state_desc <> 'READ_WRITE') RAISERROR('Query Store не записывает данные', 16, 1);

Выполняйте эту проверку как шаг задания SQL Server Agent и настройте задание на уведомление кого-либо при сбое шага. RAISERROR сам по себе не отправляет электронное письмо.

Заодно проверьте, что уже принудительно зафиксировано, включая планы с зафиксированными сбоями принуждения:

SELECT p.query_id, p.plan_id, p.is_forced_plan, p.plan_forcing_type_desc, p.force_failure_count, p.last_force_failure_reason_desc FROM sys.query_store_plan AS p WHERE p.is_forced_plan = 1 OR p.force_failure_count > 0;

Ненулевой force_failure_count означает, что принуждение плана не удалось по крайней мере один раз. Счётчик увеличивается, когда принуждение не удаётся во время перекомпиляции, а не при каждом выполнении. Проверьте last_force_failure_reason_desc, текущий план и недавние компиляции, прежде чем предполагать, что запрос всё ещё защищён.

Проверка 3: параллелизм без нашествия рабочих процессов

SELECT name, value_in_use FROM sys.configurations WHERE name IN ('cost threshold for parallelism', 'max degree of parallelism');

Порог стоимости по-прежнему по умолчанию равен 5. Пять — это заводское значение по умолчанию, а не рекомендация и уж точно не семейная традиция. SQL Server рассматривает параллельные альтернативы, когда расчётная стоимость наилучшего последовательного плана превышает порог. Эта стоимость — оценка оптимизатора, а не секунды. Если порог слишком низок для OLTP-нагрузки, слишком много скромных запросов могут получить параллельные планы и способствовать давлению на рабочие процессы. Ожидания CXPACKET и CXCONSUMER — это данные для интерпретации, а не диагноз сами по себе.

DECLARE @target_cost_threshold int = 20; -- Пример. Выберите на основе данных. DECLARE @apply_change bit = 0; -- Измените на 1 только после утверждения. SELECT value_in_use AS current_value, @target_cost_threshold AS proposed_value, @apply_change AS apply_change FROM sys.configurations WHERE name = 'cost threshold for parallelism'; IF @apply_change = 0 RETURN; IF @target_cost_threshold NOT BETWEEN 0 AND 32767 THROW 50000, 'Выберите порог стоимости от 0 до 32767.', 1; DECLARE @advanced_options_was_on bit = ( SELECT CONVERT(bit, value_in_use) FROM sys.configurations WHERE name = 'show advanced options' ); IF @advanced_options_was_on = 0 BEGIN EXEC sys.sp_configure 'show advanced options', 1; RECONFIGURE; END; EXEC sys.sp_configure 'cost threshold for parallelism', @target_cost_threshold; RECONFIGURE; IF @advanced_options_was_on = 0 BEGIN EXEC sys.sp_configure 'show advanced options', 0; RECONFIGURE; END;

Скрипт намеренно безопасен по умолчанию и использует 20 только как пример. Повышайте порог небольшими, проверенными шагами, наблюдайте за полным бизнес-циклом и сравнивайте данные Query Store до и после. Устанавливайте MAXDOP также осознанно, исходя из версии SQL Server, доступных логических процессоров, архитектуры NUMA и поведения рабочей нагрузки, а не предполагая, что 0 подходит.

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

Проверка 4: tempdb — проверьте ожидания перед добавлением файлов

SELECT file_id, name, type_desc, size / 128.0 AS size_mb, CAST(CASE WHEN is_percent_growth = 1 THEN growth ELSE growth * 8.0 / 1024 END AS decimal(18,2)) AS growth_value, CASE WHEN is_percent_growth = 1 THEN 'PERCENT' ELSE 'MB' END AS growth_unit FROM tempdb.sys.database_files;

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

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

SELECT session_id, wait_type, wait_duration_ms, blocking_session_id, resource_description FROM sys.dm_os_waiting_tasks WHERE wait_type LIKE 'PAGELATCH[_]%' AND resource_description LIKE '2:%';

2:1:1, 2:1:2 и 2:1:3 — известные примеры первых страниц для PFS, GAM и SGAM в первом файле данных tempdb. Страницы выделения повторяются позднее в каждом файле, так что эти три номера страниц — примеры, а не полный детектор. Это DMV также является мгновенным снимком. Опрашивайте его многократно и ищите устойчивые ожидания PAGELATCH на страницах выделения tempdb, прежде чем менять количество файлов. Один скриншот — это подсказка. Повторяющийся паттерн — это доказательство.

Проверка 5: статистика — устаревшая означает диагноз

SELECT name, is_auto_create_stats_on, is_auto_update_stats_on, is_auto_update_stats_async_on FROM sys.databases WHERE database_id > 4;

Автоматическое создание и автоматическое обновление почти всегда должны быть включены. Интересная настройка — is_auto_update_stats_async_on. Когда она выключена, запрос, вызывающий автоматическое обновление статистики, ждёт завершения обновления, прежде чем продолжить компиляцию. Включение позволяет избежать этой синхронной задержки, но вызвавший запрос компилируется с существующей статистикой, пока обновление выполняется в фоне. Это компромисс, а не универсальная рекомендация. В SQL Server 2022 и новее также оценивайте ASYNC_STATS_UPDATE_WAIT_AT_LOW_PRIORITY для снижения конкуренции за блокировки от фонового обновления.

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

SELECT OBJECT_SCHEMA_NAME(s.object_id) AS schema_name, OBJECT_NAME(s.object_id) AS table_name, s.name AS stats_name, sp.last_updated, sp.rows, sp.rows_sampled, sp.modification_counter FROM sys.stats AS s CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp WHERE sp.rows > 1000000 AND sp.modification_counter > sp.rows * 0.1 ORDER BY sp.modification_counter DESC;

Счётчик изменений (modification_counter) отслеживает изменения в первом столбце статистики. Фильтр 10 процентов — это эвристика для сортировки, а не внутренний порог автоматического обновления SQL Server. Большой счётчик также не доказывает, что устаревшая статистика вызвала медленный запрос. Сравните rows_sampled с rows, затем изучите оценочное и фактическое количество строк для затронутого запроса. Целевое обновление с более высоким процентом выборки может помочь, если статистика действительно является причиной. Полное сканирование всех больших таблиц — это способ превратить задачу обслуживания в следующий отчёт об инциденте.

Проверка 6: размер меняет значение слова «безопасно»

Большинство самых худших инцидентов, которые я расследую, восходят к изменению, которое было технически корректным, но выполнено в неправильном масштабе. Инструкция была верной. Таблица была огромной. SQL Server учёл оба факта.

Вам не нужна новая платформа для начала. Вам нужен список, опубликованный до окна изменений, таблиц, в которых ничего «обычного» не происходит:

WITH table_footprint AS ( SELECT object_id, SUM(CASE WHEN index_id IN (0, 1) THEN row_count ELSE 0 END) AS row_count, SUM(reserved_page_count) * 8 / 1024.0 AS reserved_mb FROM sys.dm_db_partition_stats GROUP BY object_id ) SELECT s.name AS schema_name, t.name AS table_name, f.row_count, f.reserved_mb FROM table_footprint AS f JOIN sys.tables AS t ON t.object_id = f.object_id JOIN sys.schemas AS s ON s.schema_id = t.schema_id WHERE f.row_count > 50000000 ORDER BY f.row_count DESC;

Количество строк является приблизительным, а reserved_mb включает индексы таблицы. Замените 50 миллионов на порог, отражающий вашу среду. Этого достаточно для создания «входного барьера» для развёртывания. Правило умещается в одном предложении: никакого потенциально блокирующего или зависящего от размера DDL для чего-либо из этого списка без письменного плана выполнения, проверенного пути отката и оценок продолжительности, роста журнала транзакций и влияния на блокировки. Это одно правило предотвратило больше сбоев для моих клиентов, чем многие куда более впечатляющие проекты.

Проверка 7: Resource Governor для рабочей нагрузки, которая считает, что владеет сервером

В каждом магазине есть такая. Отчётная нагрузка в конце месяца приходит, «укладывает» транзакционную нагрузку, её расследуют, объясняют, а затем она возвращается в следующем месяце, как собрание, которое никто не осмелился отклонить.

Управление этой нагрузкой поддерживается там, где позволяет редакция. Значения ниже — это заполнители для проверенной политики, а не рекомендуемые рабочие значения. MAX_CPU_PERCENT — это оппортунистический максимум, который применяется при конкуренции за CPU, в то время как CAP_CPU_PERCENT — жёсткий потолок CPU. Для обычных дисковых нагрузок MAX_MEMORY_PERCENT управляет рабочей памятью запросов для пула, а не общей памятью SQL Server или буферным пулом. Таблицы, оптимизированные для памяти, имеют дополнительное поведение пула, которое должно оцениваться отдельно.

USE master; GO CREATE RESOURCE POOL ReportingPool WITH (MAX_CPU_PERCENT = 25, CAP_CPU_PERCENT = 40, MAX_MEMORY_PERCENT = 25); CREATE WORKLOAD GROUP ReportingGroup USING ReportingPool; GO CREATE FUNCTION dbo.fn_ClassifyWorkload() RETURNS SYSNAME WITH SCHEMABINDING AS BEGIN RETURN CASE WHEN SUSER_SNAME() = N'DOMAIN\ReportingService' THEN N'ReportingGroup' ELSE N'default' END; END; GO ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = dbo.fn_ClassifyWorkload); ALTER RESOURCE GOVERNOR RECONFIGURE;

Это иллюстративная новая конфигурация. Если на сервере уже есть функция классификатора, добавьте правило маршрутизации в эту функцию вместо замены её этим примером. Классификатор принадлежит базе данных master и оценивается для каждого нового сеанса, даже когда включено пулирование соединений. Повторное использование существующего пулированного сеанса не создаёт новое событие классификации, и существующие сеансы сохраняют свою текущую группу. Держите функцию простой, тестируйте с действительно новым подключением, проверяйте назначенную группу рабочей нагрузки и подтверждайте доступ к выделенному административному подключению перед развёртыванием. Чрезмерно ограничивающий пул может заставить запрос выполняться дольше и удерживать блокировки дольше, поэтому проверяйте влияние на защищаемую транзакционную нагрузку так же тщательно, как и на отчётность.

Проверка 8: оповещайте о дыме, а не о пепле

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

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

SELECT r.session_id, r.blocking_session_id, r.wait_time / 1000 AS wait_seconds, r.wait_type, r.wait_resource, DB_NAME(r.database_id) AS database_name, t.text AS running_sql FROM sys.dm_exec_requests AS r OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) AS t WHERE r.blocking_session_id <> 0 AND r.wait_time > 30000;

Положительные значения blocking_session_id идентифицируют другой сеанс. Отрицательные значения имеют специальные значения, включая осиротевшие распределённые транзакции и владельцев кратких блокировок, которых SQL Server не может идентифицировать. В частности, -5 сам по себе не доказывает проблему с производительностью. Этот запрос — сигнал раннего предупреждения, а не полный анализ цепочки блокировок.

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

SELECT d.name AS database_name, ls.total_log_size_mb, ls.active_log_size_mb, CAST(100.0 * ls.active_log_size_mb / NULLIF(ls.total_log_size_mb, 0) AS decimal(6,2)) AS active_log_percent, ls.log_since_last_log_backup_mb, ls.log_truncation_holdup_reason FROM sys.databases AS d CROSS APPLY sys.dm_db_log_stats(d.database_id) AS ls WHERE d.state_desc = 'ONLINE' AND d.database_id > 4 ORDER BY active_log_percent DESC;

sys.dm_db_log_stats доступен в SQL Server 2016 SP2 и новее. На вторичной реплике группы доступности функция возвращает только подмножество своих обычных столбцов, поэтому отсутствующие значения размера не следует интерпретировать как нулевое давление. Запрос показывает текущее давление и причину задержки усечения, а не историю событий роста файлов. Захватывайте авто-рост отдельно с помощью Extended Events или вашей платформы мониторинга. Сигнал предупреждения часто виден до того, как сработает пейджер, но только если вы сохраняете базовую линию и оповещаете об устойчиво аномальных значениях.

Почему ничего из этого не делается

Первый аудит занимает около часа. Большая часть исправлений — это настройка, код и дисциплина эксплуатации с использованием возможностей, которыми организация уже владеет. Функции, зависящие от редакции, всё равно нужно проверять, прежде чем кто-либо пообещает изменение. Так почему же такой большой пласт практической профилактики остаётся неиспользованным?

Потому что профилактика невидима, а у невидимой работы нет защитника.

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

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

Никто здесь не ведёт себя иррационально. Стимулы направлены на огонь, а не на проводку, и так было долгое время.

Мой практический совет — описывать профилактику на языке инцидента, который она устраняет. Не «я включил автоматическую настройку». Вместо этого: «регрессии планов, подпадающие под критерии, теперь могут автоматически исправляться и проверяться после принуждения». Не «я добавил оповещение». Вместо этого: «мы теперь обнаруживаем устойчивые блокировки, пока ещё есть время действовать». Та же техническая работа, гораздо более чёткая бизнес-ценность.

Где у этого аргумента есть границы

Здесь уместны две честные оговорки, и вторая — более сильная.

У профилактики есть потолок. Вы можете устранить известные классы отказов. Вы не можете устранить беспрецедентное. Мониторинг и объяснение никогда не будут стоить ноль, и любой, кто обещает мир без инцидентов, что-то вам продаёт.

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

Так что утверждение уже, чем предполагает заголовок. Объяснение необходимо. Рассматривать объяснение как конечную цель — вот ошибка.

Раздел, который я теперь добавляю к каждому анализу

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

В нём назван класс отказов, указано, что сделало бы его невозможным, а не просто видимым, и дана примерная стоимость. Иногда стоимость — это день и флажок. Иногда — это запланированный проект. И то, и другое легче финансировать, когда стоимость и устраняемый отказ явно указаны.

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

Нить, проходящая через всё это, — кто берёт на себя суждение, когда инструментарий звучит уверенно, что является темой всех тридцати эссе в моей книге AI: Nobody's in There: Но мы всё ещё здесь. Все тридцать доступны для бесплатного чтения на pinaldave.com. Если вы предпочитаете держать копию в руках, она доступна на Amazon в мягкой обложке, Kindle и аудиокниге.

Если вы возьмёте что-то одно из этого, возьмите самое маленькое. Запишите версию и редакцию, затем выполните запрос состояния Query Store из проверки 2 для вашей самой загруженной базы данных. Это займёт около двух минут. Если ответ — READ_ONLY, самая ценная история производительности на сервере может уже бесшумно исчезать.

Это не история о том, как лучше объяснять инциденты, это история о том, чтобы сделать объяснение тем, что требуется реже.

Комментариев нет:

Отправить комментарий