96.2
Karma
69.8
Rating
Егор Рогов @erogov

Пользователь

WAL в PostgreSQL: 3. Контрольная точка

+1

Закрепление (aka pin) используется, когда процесс работает со страницей. При этом страницу можно не только читать, но и изменять в некоторых пределах. Но вытеснять нельзя — закрепление этого не позволяет.


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

WAL в PostgreSQL: 3. Контрольная точка

+1

Нет, никаких других ограничителей, кроме названных, нет.


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


А деградация (как внезапный провал) будет в том случае, если ОС долго откладывает физическую запись на диск и потом начинает писать сразу и много. Мне это так видится.


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

WAL в PostgreSQL: 3. Контрольная точка

+1

Ускорение возможно за счет того, что в обычном режиме контрольная точка не пишет данные на максимальной скорости. Она должна успеть записать все грязные буферы за время checkpoint_timeout × checkpoint_completion_target. Процесс сам регулирует задержки, чтобы уложиться в этот интервал. А когда надо ускориться, уменьшает задержку.


Проблема будет в том случае, когда даже на максимальной скорости контрольная точка не успевает уложиться в заданные рамки. Тогда надо либо сознательно увеличивать интервал (если он неоправданно маленький), либо колдовать с настройками ОС и железом. А со стороны PostgreSQL там оптимизировать-то нечего. Только не делать контрольные точки чаще, чем нужно.

WAL в PostgreSQL: 3. Контрольная точка

+1

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

WAL в PostgreSQL: 3. Контрольная точка

+1

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

WAL в PostgreSQL: 3. Контрольная точка

+1

Это, мне кажется, неправильная постановка вопроса. Если у нас работает контрольная точка (неспеша) и мы видим, что размер журнала приближается к пороговому значению, то нам не надо ни прерывать текущую точку, ни сразу после нее запускать следующую. Надо просто продолжить выполнение текущей, но ускориться.
Там какой-то такой адаптивный механизм и реализован. Деталей я сходу не вспомню, но если интересно, могу покопаться.

WAL в PostgreSQL: 3. Контрольная точка

+1

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

WAL в PostgreSQL: 3. Контрольная точка

WAL в PostgreSQL: 2. Журнал предзаписи

+1

Спасибо, Владимир.
Номер транзакции — да, используется при восстановлении (например, для установки xmin/xmax при проигровании записи о вставках/удалениях/обновлениях табличных строк).
Дружат нормально. Заморозка — это же некоторое действие со страницей, которое тоже попадает в журнал. При восстановлении мы сначала получим страницу в том виде, в котором она была до заморозки, потом восстановим заморозку и т. д.

WAL в PostgreSQL: 1. Буферный кеш

+1

Честно говоря, не копал так глубоко, но на мой взгляд нет никакого резона очищать буферы. Пригодятся страницы — ну хорошо, не пригодятся — будут вытеснены и заменены на что-то полезное. Зачем делать лишние действия?

WAL в PostgreSQL: 1. Буферный кеш

+1

1.


Вытеснение начинается только когда нет свободных буферов, а в буферы надо поднять страницу

Да. До этого используются буферы из списка свободных.


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

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


То есть скорость пробегания полного круга существенно зависит от количества «уже нулевых счетчиков» на пути алгоритма.

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


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

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


2.


Правильно ли я понял, что алгоритм вытеснения выполняет 2 задачи сразу:
  • Уменьшает счетчики
  • Вытесняет нулевые буферы

Я к этому так подхожу: основная задача алгоритма вытеснения — найти подходящий для вытеснения буфер. А счетчики — это уже детали реализации.


3.


Фоновый процесс записи грязных страниц на диск. Использует ли он как-то информацию о счетчиках обращений?

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

WAL в PostgreSQL: 1. Буферный кеш

WAL в PostgreSQL: 1. Буферный кеш

+1

1.
Имеются в виду одиночные операторы, которые "перелопачивают" большой объем данных. На всю транзакцию (и тем более на сеанс) кольцо не выделяется.


2.
Самая массовая операция — сканирование таблицы (seq scan). Их в принципе может быть много одновременно, но для них выделяется небольшое кольцо.
Другие операции — vacuum, create table as select, copy from и, насколько я понимаю, любые операции, перезаписывающие полностью таблицу (типа vacuum full или некоторых форм alter table). Но это все операции нечастые.
Так что вряд ли нехватка места представляет опасность.
Про буферные кольца еще можно почитать в README (ну и в коде, конечно).

WAL в PostgreSQL: 1. Буферный кеш

+1

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


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

WAL в PostgreSQL: 1. Буферный кеш

+1

Ну, это общая тенденция, все СУБД стараются перейти на самостоятельное управление дисковым вводом-выводом. Пользоваться ОС удобно, но она универсальна и не может учитывать всех особенностей именно баз данных. Но чтобы от нее отказаться — надо много сил положить.
А конкретных цифр я не скажу, не знаю.

WAL в PostgreSQL: 1. Буферный кеш

+1

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

Блеск и нищета переводной литературы

Блеск и нищета переводной литературы

+3

У нас при переводе документации по PostgreSQL (https://postgrespro.ru/docs) тоже было много дискуссий и сомнений.
В результате подготовили глоссарий, которым сами пользуемся и всем рекомендуем: https://postgrespro.ru/education/glossary
Если будет желание обсудить, можно со мной или с xclusn — Александр как раз и занимается переводом.

WAL в PostgreSQL: 1. Буферный кеш

WAL в PostgreSQL: 1. Буферный кеш

0

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

WAL в PostgreSQL: 1. Буферный кеш

0

В нашем деле всегда приходится изучать что-то новое, зато не скучно.

WAL в PostgreSQL: 1. Буферный кеш

Indexes in PostgreSQL — 10 (Bloom)

MVCC в PostgreSQL-8. Заморозка

+1

Владимир, спасибо и вам за содержательные вопросы!


* снимаются биты — фиксации и отмены, потому что они используются в MVCC

Неа.


Мы замораживаем xmin: устанавливаем биты xmin committed + xmin aborted. Это означает, что транзакция, которая создала данную версию строки, произошла так давно, что ее номер нас больше не интересует.


Если мы затем изменяем строку, то устанавливаем xmax, чтобы показать, что время действия этой (теперь уже не актуальной) версии закончилось. Но xmin и его биты не трогаем — зачем? Нас устраивает, что эта версия началась в незапамятные времена.


А xmin будет установлен у новой версии этой строки.

MVCC-7. Автоочистка

+1
Создается ли новая версия TOAST только для JSONB?

Да.


То есть TOAST это таблица, где данные хранятся в виде ключ-значение?

Ммм?


И каждой строке основной таблицы соответствует 2 строки TOAST таблицы (на каждое из полей)?

Не обязательно. В toast-таблице будет минимум одна строка на каждый "длинный" атрибут, который не сжался и попал в тостер.


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


=# CREATE TABLE ttt(a text, b text);

Пусть два текстовых поля будут для простоты, JSONB ничем не отличается. Чтобы не воевать со сжатием, поставим стратегию external:


=# ALTER TABLE ttt ALTER COLUMN a SET STORAGE EXTERNAL;
=# ALTER TABLE ttt ALTER COLUMN b SET STORAGE EXTERNAL;

Вставляем что-нибудь длинное. 3000 байт будут порезаны на два кусочка.


=# INSERT INTO ttt VALUES (repeat('a',3000), repeat('b',3000));

Находим toast-таблицу:


=# SELECT relnamespace::regnamespace, relname
FROM pg_class WHERE oid = (
  SELECT reltoastrelid FROM pg_class WHERE relname = 'ttt'
);

 relnamespace |    relname     
--------------+----------------
 pg_toast     | pg_toast_49334
(1 row)

Вот что мы там видим (сами данные не показываю):


=# SELECT chunk_id, chunk_seq FROM pg_toast.pg_toast_49334;

 chunk_id | chunk_seq 
----------+-----------
    49340 |         0
    49340 |         1
    49341 |         0
    49341 |         1
(4 rows)

Тут у нас два фрагмента (chunk_id = 49340 и 49341), в каждом — два кусочка (chunk_seq).
Теперь обновим одно поле:


=# UPDATE ttt SET a = a || 'a';
=# SELECT chunk_id, chunk_seq FROM pg_toast.pg_toast_49334;

 chunk_id | chunk_seq 
----------+-----------
    49341 |         0
    49341 |         1
    49342 |         0
    49342 |         1
(4 rows)

Фрагмент 49341 (который, очевидно, соответствует полю b) остался, а вместо 49340 (поле a) имеем 49342.

MVCC-7. Автоочистка

+1

Все верно, блокирует. Но по одной страничке за раз и ненадолго. И этим не особо отличается от любого другого процесса, который что-то меняет в таблице.


Я тут имел в виду, что таблица не блокируется, скажем, целиком на все время очистки.

MVCC-7. Автоочистка

+1
А где хранится информация о том, когда именно была изменена строка?

А такая информация и не хранится. Нет, ну по xmax можно что-то погадать, но это все не нужно.


Просто каждая транзакция в конце работы отчитывается коллектору статистики: вставила, мол, столько-то строк в такой-то таблице, да столько-то удалила, да еще столько-то изменила. А коллектор эти цифры агрегирует и складывает (см. pg_stat_all_tables).


Поэтому мы там имеем, в числе прочего, примерное общее число измененных строк (поле n_mod_since_analyze). А когда приходит анализ, он это поле обнуляет.


Как именно выбрираются строки, которые изменились после?

Никак. Их постфактум не вычислить. Да и не нужно.

MVCC-7. Автоочистка

MVCC-7. Автоочистка

+1

Спасибо, Владимир.


Получается, есть еще какой-то фоновый сборщик статистики?

Да. Поленился я начать с самого начала, поэтому обошел это вниманием…
Есть такой процесс — stats collector. Он накапливает статистику, которую постоянно получает от всех процессов, агрегирует ее и показывает в представлениях pg_stats_*. Это не та статистика, что использует планировщик и которая собирается командой ANAYZE. Подробно все это рассматривается в курсе DBA1, тема Мониторинг.

MVCC-2. Слои, файлы, страницы

0

Ну да, что выросло — то выросло. Другие языки есть (например), но чтобы сейчас подвинуть глыбу SQL, этого явно недостаточно.

MVCC-2. Слои, файлы, страницы

+1
Я порылся в старых словарях, и выходит, что отношение и кортеж — термины реляционной алгебры. Кроме того, кортеж упоминается еще в советских учебниках по комбинаторике как упорядоченный набор элементов. Словом, это вполне себе устоявшийся термин.

Ряд (строка в вашем варианте) и столбец — термины уже из SQL. Лично мне строка слегка режет глаз, т.к. слово это обычно не употребляется не для описания таблиц, а именно строк, например, текста. Насколько понимаю, table/row/column в SQL попали для того, чтобы потенциальным пользователям-непрограммистам было легче освоить язык (см. "Early History of SQL" самого Чемберлейна).

Полностью со всем согласен! Но дальше мы из одних и тех же посылок делаем разные выводы (:


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


Кстати, ранней историей SQL я в свое (тогда еще оракловое) время интересовался, и написал по горячим следам несколько постов в блоге. Но снаружи не публиковал нигде, больше для себя, чтобы разобраться.

MVCC-2. Слои, файлы, страницы

0

Спасибо.


По-английски tuples и row versions (в контексте Постгреса, конечно) используются как синонимы. Соответственно по-русски я предпочитаю версия строки (или просто строка, если речь не о версионности), потому что такой термин не требует специального объяснения. В то время как кортеж — просто неудачное (на мой взгляд) заимствование из реляционной теории.


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

MVCC-6. Очистка

0
Реплика начала отставать все больше и больше с каждым часом, потому что запрос Васи затронул очень много данных в таблице orders.

Тут логическая ошибка. Реплика будет отставать не из-за того, что кто-то затронул какие-то данные. У запросов на реплике есть обычные снимки, вот они и используются, чтобы понять, конфликтует журнальная запись или нет. Так же, как на мастере.


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

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


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


Да, ещё можно настроить max_standby_streaming_delay так, чтобы конфликтующие записи применялись, но с задержкой. Тогда реплика будет отставать не больше, чем на это значение, а у более долгих запросов будет шанс доработать, если они не «наступят» на отсутствующие данные.

MVCC-6. Очистка

0

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

MVCC-6. Очистка

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

Нет, хранить будет реплика.


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

Если отчёты работают на мастере, то WAL избыточно хранить не надо. Проблема будет только в откладывании очистки и распухании таблиц.


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

Это мы уже про реплику говорим, правильно?
Сами по себе отчёты (=запросы) конкурировать не должны, они выполняются одновременно. При этом реплика может откладывать применение журнальных записей (обычно связанных с очисткой), которые несовместимы с запросами. Ну да, будет отставание. Его можно мониторить.


Тогда получается, что нужно делать даже 2 реплики — одну для очень долгих отчетов, другую — для отчетов но покороче.

Это, мне кажется, перебор, слишком сложно. Короткие отчёты и на мастере никому сильно не помешают.


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

MVCC-6. Очистка

0

Это работает HOT, пытаясь удержать обновления в пределах той же страницы.
Думаю, что инструмент использует не SQL UPDATE, а что-то более низкоуровневое. Я не смотрел, но пожалуй надо будет разобраться.

MVCC-6. Очистка

+1

Скажу честно — не знаю, есть ли реальные примеры успешного применения. Мне не попадались.

MVCC-6. Очистка

+1

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

MVCC-6. Очистка

Indexes in PostgreSQL — 8 (RUM)

0
You are welcome!

Nope. In fact rum doesn't even support json. It was designed with full text search in mind.
1 There