Pull to refresh

Comments 23

Спасибо вам большое за такие шикарные серии статей (а также за прекрасные учебные материалы на сайте)!

На здоровье, для хороших людей ничего не жалко! (:

Спасибо за достаточно подробное описание и примеры. Так получилось, что сейчас идет исход по возможности некоторых приложений из Oracle в PostgreSQL, приходится изучать новое.

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

Вы очень точно указали на особенность про пробег кругов, за что спасибо — внятно все рассказали. В Oracle есть списки LRU — тоже там не все так весело. С таким пробегом по кругу, чем больше размер буферного кеша, тем может быть хуже для производительности. Чем-то напоминает стоимость обслуживания shared pool в Oracle, чего нельзя сказать там о буферном кэше: есть смысл выделять по максимуму, по размеру оперативной части базы. Тут же все немного не так. Интересно, надо будет еще почитать. Теперь будет все через призму сравнения идти.
А вот есть ли в PostgreSQL некий аналог буфера журналов (WAL)?

Очень жду продолжения, важная тема для администратора. Сохранит здоровый сон… или подарит бессонницу ;)

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

Чтение с диска через файловый кэш ос в буферный кэш базы можно как то избежать? Использовать direct io?

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

А насколько переход на directIO способен увеличить производительность? Действительно ли если постгрес возьмет на себя часть функций ОС — это будет хорошим решением? Не идем ли мы таким образом в сторону, когда постгрес — это и есть операционная система, ведь она берет часть ее функций на себя.

И это может значительно замедлить скорость развития самой Постгрес.

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

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

Ну вот например та же MySQL умеет DirectIO, не говоря уже о базах большой тройки (и не только). Вообще в случае с PostgreSQL создается ощущение что для разработки там делается очень много, но вот некоторые части совсем не трогают.
Егор, очень рад продолжению цикла статей, спасибо за труд.

Интересное подозрение возникло при чтении особенностей вытеснения.
1. Пусть нужно прочитать страницу в буфферный пул с целью ее изменить — например, добавить новую строку в таблицу
2. Находим свободный слот и пусть этот слот был последним из свободных. Пишем страницу в него.
3. Изменяем страницу — добавляем строку. Отпускаем buffer pin блокировку.
4. Счетчик буфферного слота стал равен 1. Может и больше, но тут важен момент что страница «свежая» и счетчик «маленький»

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

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

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

На ум приходит идея дать «буферам-новичкам» бонус в виде базового значения счетчика не 1 а например 5.

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


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

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


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

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

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

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

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

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

1.


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

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


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

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


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

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


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

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


2.


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

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


3.


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

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

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


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

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


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

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


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

Спасибо, а как происходит освобождение «уже ненужного» буфферного кольца. Буфферы, ранее выделенные на кольцо, полностью очищаются? Ведь насколько я понял трудно будет «убрать кольцо и сделать буфферы кольца доступными для всех», ведь буферы кольца узко специализированы были под конкретный массовый оператор. И врят ли будут полезны другим транзакциям.

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

Шикарная статья, спасибо за Ваш труд и ждем продолжения.

Спасибо! Оставайтесь с нами, продолжение будет.

Запишу тут еще про массовое вытеснение (aka вымывание кеша). Несмотря на буферные кольца, оно вполне возможно в ряде ситуаций.


  • Если при последовательном сканировании (Seq Scan) страницы изменяются, то они "отцепляются" от буферного кольца, а к кольцу добавляется новая страница (она выбирается обычным алгоритмом поиска очередной "жертвы"). То есть фактически буферное кольцо не работает. Это имеет место, когда мы обновляем много строк в таблице командой UPDATE.
  • Если мы читает данные из toast-таблицы, это всегда происходит по индексу независимо об объема. Тут буферное кольцо вообще не используется.

С другой стороны, это не значит, что в буферном кеше моментально не останется нужных данных. Буферы со счетчиком обращений 0 или 1, конечно, будут вытеснены, но горячие буферы имеют шанс сохраниться.

Sign up to leave a comment.