amcheck

Модуль amcheck предоставляет функции, позволяющие проверять логическую целостность структуры отношений.

Функции проверки B-дерева проверяют различные инварианты в структуре представления определенных отношений. Корректность работы функций метода доступа, стоящих за сканированием индекса и другими важными операциями, зависит от всегда соблюдаемых инвариантов. Например, определенные функции проверяют, в том числе, что все страницы B-дерева содержат элементы в «логическом» порядке (например, у индексов B-деревьев по столбцу text кортежи должны быть упорядочены в лексическом порядке по правилу сортировки). Если этот конкретный инвариант каким-то образом нарушается, можно ожидать, что двоичный поиск на затронутой странице задаст ошибочное направление сканированию индекса, приведя к неверным ответам на запросы SQL. Если структура оказывается допустимой, ошибок не возникает.

Проверка осуществляется теми же процедурами, которые используются при сканировании индекса, и это может быть код пользовательского класса операторов. Например, проверка индекса B-дерева прибегает к сравнениям, выполняемым одной или несколькими вспомогательными функциями B-дерева под номером 1. Подробнее вспомогательные функции классов операторов описываются в подразделе Вспомогательные процедуры индексного метода.

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

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


Функции

bt_index_check(index regclass, heapallindexed boolean) returns void

bt_index_check проверяет, соблюдаются ли в целевом объекте, индексе B-дереве, различные инварианты. Пример использования:

test=# SELECT bt_index_check(index => c.oid, heapallindexed => i.indisunique),
               c.relname,
               c.relpages
FROM pg_index i
JOIN pg_opclass op ON i.indclass[0] = op.oid
JOIN pg_am am ON op.opcmethod = am.oid
JOIN pg_class c ON i.indexrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE am.amname = 'btree' AND n.nspname = 'pg_catalog'
-- Не проверять временные таблицы, т. к. они могут относиться к другим сеансам:
AND c.relpersistence != 't'
-- Без этих условий функция может выдать ошибку:
AND c.relkind = 'i' AND i.indisready AND i.indisvalid
ORDER BY c.relpages DESC LIMIT 10;
 bt_index_check |             relname             | relpages
----------------+---------------------------------+----------
                | pg_depend_reference_index       |       43
                | pg_depend_depender_index        |       40
                | pg_proc_proname_args_nsp_index  |       31
                | pg_description_o_c_o_index      |       21
                | pg_attribute_relid_attnam_index |       14
                | pg_proc_oid_index               |       10
                | pg_attribute_relid_attnum_index |        9
                | pg_amproc_fam_proc_index        |        5
                | pg_amop_opr_fam_index           |        5
                | pg_amop_fam_strat_index         |        5
(10 rows)

Этот пример демонстрирует сеанс проверки 10 самых больших индексов каталогов в базе данных «test». Проверка наличия кортежей кучи в виде индексных кортежей запрашивается только для тех из этих индексов, которые являются уникальными. Поскольку ошибки не было, все проверенные индексы представляются логически целостными. Естественно, этот запрос легко можно изменить, чтобы функция bt_index_check вызывалась для всех индексов в базе данных, которые поддерживают эту проверку.

Функция bt_index_check запрашивает блокировку AccessShareLock для целевого индекса и отношения в куче, которому он принадлежит. Это тот же режим блокировки, что запрашивается для отношений обычными операторами SELECT. bt_index_check не проверяет инварианты, охватывающие взаимоотношения потомок/родитель, но проверит наличие всех кортежей кучи в индексе в виде индексных кортежей, когда параметр heapallindexed равен true. Когда в работающей производственной среде требуется регулярная упрощенная проверка, использование bt_index_check часто предоставляет наилучший компромисс между полнотой проверки и ограничением влияния на производительность и доступность приложения.

bt_index_parent_check(index regclass, heapallindexed boolean, rootdescend boolean) returns void

Функция bt_index_parent_check проверяет, соблюдаются ли в целевом объекте, индексе B-дереве, различные инварианты. Кроме того, когда аргумент heapallindexed равен true, эта функция проверяет наличие всех кортежей кучи, которые должны в нем находиться. Когда необязательный аргумент rootdescend равен true, при проверке для каждого кортежа на уровне листьев проводится повторный поиск, начиная с корневой страницы. Проверки, которые может осуществить bt_index_parent_check, являются расширенным набором проверок, выполняемых функцией bt_index_check. Функцию bt_index_parent_check можно считать более полным вариантом bt_index_check: в отличие от bt_index_check, bt_index_parent_check проверяет еще и инварианты, которые охватывают взаимоотношения потомок/родитель, в том числе на отсутствие потерянных связей в структуре индекса. bt_index_parent_check следует общему соглашению о выдаче ошибки в случае обнаружения логической несогласованности или другой проблемы.

Функция bt_index_parent_check запрашивает в целевом индексе блокировку ShareLock (которая также запрашивается в отношении в куче). Эти блокировки предотвращают одновременное изменение данных командами INSERT, UPDATE и DELETE. Эти блокировки также предотвращают одновременную обработку нижележащего отношения командой VACUUM и другими сервисными командами. Обратите внимание, что эта функция удерживает блокировки только во время выполнения, а не на протяжении всей транзакции.

Дополнительная проверка, проводимая функцией bt_index_parent_check, в большей степени ориентирована на выявление различных патологических случаев. Такие случаи могут включать в себя неправильно реализованный класс операторов B-дерева, используемый проверяемым индексом, или, гипотетически, неизвестные ошибки в нижележащем коде индексного метода доступа B-дерева. Обратите внимание, что bt_index_parent_check нельзя применять, когда включен режим горячего резерва (т. е. на физических репликах в режиме «только чтение»), в отличие от bt_index_check.

Совет
Функции bt_index_check и bt_index_parent_check выводят сообщения журнала о процессе проверки на уровнях важности DEBUG1 и DEBUG2. Эти сообщения содержат подробную информацию о процессе проверки, которая может представлять интерес для разработчиков QHB. Также эта информация может быть полезна для продвинутых пользователей, поскольку обеспечивает дополнительный контекст в случае, если при проверке действительно обнаружится несогласованность. Чтобы получать сообщения о процессе проверки с подходящим уровнем детализации, необходимо перед запуском проверяющего запроса выполнить в интерактивном сеансе psql:

 SET client_min_messages = DEBUG1;

verify_heapam(relation regclass, on_error_stop boolean, check_toast boolean, skip text, startblock bigint, endblock bigint, blkno OUT bigint, offnum OUT integer, attnum OUT integer, msg OUT text) returns setof record

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

Принимаются следующие необязательные аргументы:

on_error_stop
Если true, проверка на наличие повреждений останавливается в конце первого блока, в котором обнаружены какие-либо повреждения.
По умолчанию false.

check_toast
Если true, значения TOAST проверяются по TOAST-таблице целевого отношения.
Эта проверка может быть медленной. Кроме того, если TOAST-таблица или ее индекс повреждены, проверка значений TOAST потенциально может привести к сбою сервера, хотя во многих случаях это просто вызовет ошибку.
По умолчанию false.

skip
Если указано значение, отличное от none, проверка на наличие повреждений пропускает блоки, помеченные как полностью видимые или полностью замороженные, в зависимости от указанного значения. Допустимые варианты: all-visible (полностью видимые), all-frozen (полностью замороженные) и none.
По умолчанию none.

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

endblock
Если этот параметр задан, проверка на наличие повреждений заканчивается на указанном блоке, пропуская все оставшиеся блоки. Если в endblock указано значение вне диапазона блоков целевой таблицы, выдается ошибка.
По умолчанию проверяются все блоки.

Для каждого выявленного повреждения verify_heapam возвращает строку со следующими столбцами:

blkno
Номер блока, содержащего поврежденную страницу.

offnum
Номер смещения поврежденного кортежа.

attnum
Номер атрибута поврежденного столбца в кортеже, если поврежден именно столбец, а не весь кортеж.

msg
Сообщение с описанием выявленной проблемы.


Дополнительная проверка heapallindexed

Когда аргумент heapallindexed функций проверки B-дерева равен true, для таблицы, связанной с отношением целевого индекса, проводится дополнительная фаза проверки. Она включает «фиктивную» операцию CREATE INDEX, которая проверяет наличие всех гипотетических новых индексных кортежей по временной сводной структуре в памяти (она создается при необходимости во время основной первой фазы проверки). Сводная структура «помечает» каждый кортеж, найденный в целевом индексе. На высоком уровне основная идея проверки heapallindexed состоит в том, что новый индекс, равнозначный уже существующему, целевому индексу, должен содержать только те записи, которые можно найти в существующей структуре.

Дополнительная фаза heapallindexed приводит к значительным издержкам: как правило, проверка выполняется в несколько раз дольше обычного. Однако никакие новые блокировки уровня отношения при проверке heapallindexed не запрашиваются.

Сводная структура ограничивается по объему значением maintenance_work_mem. Чтобы убедиться, что вероятность пропустить несогласованность в кортежах кучи, которые должны быть представлены в индексе, составляет менее 2%, требуется приблизительно 2 байта памяти на кортеж. По мере уменьшения объема памяти в пересчете на кортеж эта вероятность медленно растет. Такой подход значительно ограничивает издержки проверки, при этом лишь немного снижая вероятность выявления проблемы, особенно в установках, где проверка включена в процедуру регулярного обслуживания. При очередной проверке возникает новый шанс выявить пропущенное единичное отсутствие или повреждение кортежа.


Эффективное использование amcheck

Модуль amcheck может быть полезен для выявления различных типов отказов, которые могут остаться незамеченными при включении контрольных сумм. В частности это:

  • Структурные несоответствия, вызванные некорректной реализацией класса операторов.

    В том числе это проблемы, вызванные изменениями правил сравнения в операционной системе. Сравнения данных сортируемого типа, например text, должны быть постоянными (как и все сравнения, применяемые при сканировании индекса B дерева), что подразумевает полную неизменность правил сортировки в операционной системе. Подобные проблемы, хоть и редко, могут возникать при обновлениях правил сортировки в операционной системе. Гораздо чаще проявляются несоответствия порядка сортировки между главным и резервным серверами, вероятно, из-за различия основной версии используемой операционной системы. Такие несоответствия обычно возникают только на резервных серверах, поэтому и выявить их обычно можно только на них.

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

  • Структурные несоответствия между индексами и проиндексированными отношениями в куче (когда выполняется проверка heapallindexed).

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

  • Повреждение, вызванное гипотетическими неизвестными ошибками в нижележащем коде методов доступа, коде сортировки или коде управления транзакциями QHB.

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

  • Ошибки в файловой системе или подсистеме хранения, когда просто не включены контрольные суммы.

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

  • Повреждение, вызванное дефектным ОЗУ или вообще подсистемой памяти.

    QHB не защищает от корректируемых ошибок памяти и предполагает, что вы будете применять в работе ОЗУ, использующее коды исправления ошибок (Error Correcting Codes, ECC), соответствующие отраслевому стандарту, или лучшую защиту. Однако память ECC memory обычно защищена только от ошибок в одном бите, и не следует считать ее абсолютной защитой от сбоев, приводящих к повреждению памяти.

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

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

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

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

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

В целом, amcheck может доказать только наличие повреждения, но не его отсутствие.


Исправление повреждений

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

Общего метода устранения проблем, выявляемых amcheck, не существует. Следует искать корень проблемы, приводящей к нарушению инварианта. Полезную роль в диагностике повреждений, которые выявляет amcheck, может сыграть модуль pageinspect. Для исправления повреждений одной лишь команды REINDEX может быть недостаточно.