Как стать автором
Обновить
22
0

Ph.D., senior Node.js developer (team lead)

Отправить сообщение
Запустившись, рабочий процесс подключается к указанной ему базе данных и начинает с того, что строит список:
всех таблиц, материализованных представлений и toast-таблиц, требующих очистки,


И снова вопрос про TOAST, немного не в тему статьи
* Пусть в таблице есть 2 поля — JSONB и TEXT.
* Пусть есть строки, в которых содержатся достаточно крупные значения в этих полях, чтобы работал TOAST
* берем одну из строк
* меняем один символ в JSONB, например в поле number_of_posts было значение 1, а стало — 2.
* Создается ли новая версия TOAST только для JSONB? То есть TOAST это таблица, где данные хранятся в виде ключ-значение?
И каждой строке основной таблицы соответствует 2 строки TOAST таблицы (на каждое из полей)?
Очистка не блокирует другие процессы, поскольку работает постранично


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


А где хранится информация о том, когда именно была изменена строка?
* Запоминается дата последнего анализа
* Как именно выбрираются строки, которые изменились после? Вероятно, запоминается xmax (или горизонт событий), а не сама дата. И мониторится
разница (дельта) между горизонтом событий и последней транзакцией, которая относится к таблице. Верно?
Число мертвых версий постоянно собирается коллектором статистики


Анализ таблиц, который осуществляет AUTOVACUUM и коллектор статистики — это разные процессы?
Егор, спасибо за статью и отдельное спасибо за темп 1 статья в неделю. Такой темп непросто выдерживать, поэтому вдвойне благодарен. Традиционные вопросы.

Процесс autovacuum launcher составляет список баз данных, в которых есть какая-либо активность. Активность определяется по статистике, а чтобы она собиралась, должен быть установлен параметр track_counts. Никогда не выключайте autovacuum и track_counts, иначе автоочистка не будет работать.


Получается, есть еще какой-то фоновый сборщик статистики? Потому что автовакуум сам по себе собирает статистику.
Вероятно не получится так, что на основе своей же статистики он решает когда запускать сбор статистики.
Еще вариант: длинные транзакции заранее планируются и запускаются по расписанию. Но тут нужно участие DBA — хотелось бы этого избежать тоже
Я немного расплывчато сформулировал кейс. Он такой:
* Пусть решили сделать «отчетную» реплику по просьбе аналитиков (и чтобы они нам не грузили мастер).
* Создали отдельного читающего юзера для всех аналитиков, поставили уровень изоляции Repeatable Read для согласованности, раздали логин-пароль (один на всех)
* Аналитики начали экспериментировать, строить свои отчеты.
* И вот аналитик Вася запускает отчет на таблице orders, длительность которого 3 часа.
* Реплика начала отставать все больше и больше с каждым часом, потому что запрос Васи затронул очень много данных в таблице orders.
* Вася не подозревал что получится такой долгий запрос и терпеливо решил подождать.
* Аналитик Петя решил посмотреть данные за последний час, он предполагает что данные актуальны и не знает о запросе Васи.
* Аналитик Петя строит аггрегаты, получает результат, не подозревая, что он пользуется устаревшими данными.
* И не дай бог это некий отчет, который будет использоваться при финансовых расчетах.

Если я все правильно понял, такая ситуация вполне имеет место быть.

Получается, что придется делать вот такое:
* Мониторим отставание (само собой)
* Учим аналитиков как смотреть свежесть данных, выводим им где-то например текущее оставание реплики в админке
* Учим аналитиков смотреть текущие транзакции, помогаем отследить «ждунов» — запускающих слишком длинные транзакции без согласования с остальными.
* Бедным аналитикам приходится кооперироваться между собой в чатиках, составлять расписания «длинных выгрузок» и т.п.

Очень неудобно. Как вариант можно сделать «отчетную реплику для коротких запросов» и «отчетную тормозящую реплику» для длинных. И даже может «финансовую реплику» куда ходить будут строго по расписанию.

Как Вам такая идея? Может есть идея получше? Кейс очень актуальный.

Сорри, что немного не в тему статьи
Будет ли мастер хранить сегмент WAL в этом случае до тех пор, пока реплика его себе не накатит?

Получается затятная ситуация:
* Очень длинные отчеты нагружают мастер. Только ли тем, что нужно хранить сегменты WAL? Не страдает ли от этого VACUUM, table bloat? Так ли критично то, что сегменты хранятся полдня? Но задержки такие идут скажем на постоянной основе.
* Очень длинные отчеты конкурируют между собой. Если один аналитик запустил отчет на полдня, то другие должны учитывать, что не могут получить актуальные данные в этот период.

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


О команде CLUSTER очень часто упоминают и постоянно критикуют ее за то, что ее эффект «выдыхается» (в силу MVCC) и за то, что она блокирует все.

Когда все-таки ее целесообразно использовать?
Егор, большое спасибо за очередную отличную статью! Вопросы :)

Поэтому в PostgreSQL плохо сочетаются OLTP- и OLAP-нагрузка в одной базе: отчеты, выполняющиеся часами, не дадут часто обновляемым таблицам вовремя очищаться. Возможным решением может быть создание отдельной «отчетной» реплики.


Но при этом физически они выглядят одинаково. Пусть на мастере совершилась очистка а на реплике идет отчетная транзакция, которая завершится через пару часов условно.

WAL sender отправляет сегмент, где страницы уже очищены. Реплика не будет его накатывать, пока транзакция не завершится? И все остальные сегменты тоже выстроятся в очередь.

Если так то отчетная реплика может существенно отставать от мастера
Цитата из статьи

TOAST-таблица используется только при обращении к «длинному» значению. Кроме того, для toast-таблицы поддерживается своя версионность: если обновление данных не затрагивает «длинное» значение, новая версия строки будет ссылаться на то же самое значение в TOAST-таблице — это экономит место.


А если происходит обратная ситуация — меняется только «длинное значение»? Что происходит? Создается новая версия строки, по сути, копия уже существующей с той лишь разницей, что ссылка будет указывать на новую версию TOAST-таблицы?

Если так, то понятно, как будет работать HOT в данном случае. Если нет — поясните, пожалуйста, механизм с TOAST.
Вероятно, следующий вопрос слишком низкоуровневый — а как фоновый AUTO VACUUM и внутристраничная очистка «делят между собой» процесс очистки? Пусть автовакуум хочет удалить цепочку, а начавшаяся внутристраничная очистка хочет цепочку продолжить.

Что будет происходить? Вероятно, ситуация решается физическими блокировками страниц?
Внутристраничная очистка и VACUUM

Получается, что последующая процесс AUTO VACUUM почистит индексные страницы и уберет unused указатели? А также удалит цепочку.
Все неактуальные версии строк (0,1), (0,2) и (0,3) очищены; после этого на освободившееся место добавлена новая версия строки (0,5).


Ради интереса я выполнил UPDATE в транзакции и потом откатил транзакцию. Строки все равно остались очищенными. То есть внутристраничная очистка от транзакции, видимо, никак не зависит. Как и проставление битов статусов транзакций. То есть, такие операции нетранзакционны, видимо, в силу того, что такая транзакционность никогда не требуется.
Егор, спасибо за статью. Особенно интересна рекомендация про fulfill фактор как возможность оптимизации производительности. Надеюсь, выпадет случай применить ее на практике.

Так как Вам удобнее отвечать на вопросы в отдельных комментариях — напишу их также отдельно.

Внутристраничная очистка убирает версии строк, не видимые ни в одном снимке (находящиеся за «горизонтом событий» базы данных, об этом мы говорили в прошлый раз), но работает строго в пределах одной табличной страницы.


Пусть изменилось поле text, которое настолько велико, что хранится в TOAST. Пусть его изменили несколько раз. Началась внутристраничная очистка. Полагаю, что для TOAST она не работает? Потому что TOAST — это размещение данных на нескольких страницах.

применяется ли MVCC к TOAST? Вероятно, это будет раскрыто в последующих статьях и вопрос преждевременный.

Спасибо, а как пул соединений поможет в данном случае? Насколько я понял, источник информации о состоянии транзакций «один на всех»

Егор, большое спасибо за статью!

Традиционно, несколько вопросов:

№1.
На уровне изоляции Read Committed снимок создается в начале каждого оператора транзакции. Такой снимок активен, пока выполняется оператор.


То есть имеем операции «создания»/«удаления» снимка. Их тем больше, чем больше операторов в транзакции

На уровнях Repeatable Read и Serializable снимок создается один раз в начале первого оператора транзакции. Такой снимок остается активным до самого конца транзакции.


То есть имеем всего одну операцию «создания»/«удаления» снимка.

Сам вопрос: Насколько затратны операции «создания»/«удаления»? Можно ли сказать, что уровни Repeatable Read и Serializable
значительно меньше нагружают сервер БД? Или разница ничтожно мала по сравнению с другими действиями для этих транзакций? Я имею ввиду порядки затрат, соизмеримые с порядками затрат оптимизатора, то есть микросекунды. Понятно, что по сравнению с IO операциями передачи данных по сети эти затраты будут ничтожно малы.

Имеется ввиду затратность по:
* RAM — насколько «тяжеловесна» информация о снимке.
* CPU/IO — насколько трудоемко создавать новые снимки (и видимо помечать неактивные к удалению).

№2.
На уровне изоляции Read Committed снимок создается в начале каждого оператора транзакции. Такой снимок активен, пока выполняется оператор.


Что означает понятие «снимок активен» в применении к Read Committed? Это означает, что при создании нового снимка, ранее созданный (например, на момент создания транзакции) уже никак не участвует в транзакции? Как удаляются такие снимки? Они помечаются к удалению или удаляются в рамках той же транзакции?

№3
такие интервалы не пересекаются, поэтому одна строка представлена в любом снимке максимум одной своей версией.


То есть невозможна ситуация, при которой xmin xmax одной и той же строки будут перезаписаны в разных транзакциях? То есть если xmin/xmax когда-либо были записаны — измениться они уже не могут (immutable). При условии, если транзакция изменившая их первой — зафиксировалась.

№4
А для DDL-запросов (тоже транзакционны) изоляции и снимки тоже используются? Насколько механизмы изоляций и снимков отличаются от уже описанных механизмов в этой и в предыдущих статьях?
Пересоздание всех индексов без блокировки: CREATE INDEX CONCURRENTLY «new» + DROP INDEX «old» + ALTER INDEX «new» RENAME TO «old».


* После подобной процедуры необходимо также запускать проверку целостности индекса, т.к. индекс в CONCURRENTLY режиме может получиться «битый»
* Не все индексы, думаю, нужно пересоздавать, а только те, которые затронуты были массовыми изменениями

Причем обе процедуры можно смело делать «на бою» без простоя.


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

У меня был случай, когда после массовой операции (вычищение устаревших данных в таблице) только на пересоздании индексов удалось выиграть 100 Гб места на диске.


Если есть возможность — опишите свой кейс более подробно, будет интересно.
Теперь немного понятнее стало, почему массовый апдейт даже небольшого числового поля (INTEGER) привел однажды к тому, что индекс «сломался» — эффективность его работы существенно (на порядок) просела и пришлось делать VACUUM FULL (не то написал) перестроение индекса с блокировкой. Кейс к сожалению (или к счастью) не мой. Но что делать и как обновлять — не понятно пока
На ум приходит ошибочный кейс «чистки БД»:
* Делаем массовый апдейт description поля типа TEXT, например вычищают XSS injections (или решили ругательные слова заменить точками постфактум, как бывает в стартапах)
* Сразу получаем минимум 2х к размеру TOAST-таблицы.
* И это место видимо без FULL VACUUM оказывается очень тяжело высвободить для ОС (и для других таблиц). Даже если обновлять пачками.

Пример несколько надуманный, так ошибиться сложно, если знать требования по безопасности. Просто для примера.

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

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность