Comments 23
В нашем деле всегда приходится изучать что-то новое, зато не скучно.
А вот есть ли в PostgreSQL некий аналог буфера журналов (WAL)?
Очень жду продолжения, важная тема для администратора. Сохранит здоровый сон… или подарит бессонницу ;)
Буферы журналов конечно есть, про них немного будет дальше.
Зато shared pool у нас нет, и после Оракла это взрывает мозг. Но потом привыкаешь.
Кстати, может будет интересно в плане сравнения: Обработка запросов в Oracle и PostgreSQL: следствия одного решения.
Неа, в настоящее время нельзя. Эта тема периодически всплывает в рассылках, все говорят о том, что это нужно, но, насколько я понимаю, переход на direct IO требует серьезных переделок внутри ядра, поскольку Постгресу придется взять на себя часть функций ОС.
И это может значительно замедлить скорость развития самой Постгрес.
UPD. На ум пока приходит выделять отдельный мощный сервер под постгрес таким образом, чтобы бОльшая часть RAM была занята файловым кешем и буфферным пулом. Тогда условно говоря файловый кеш ОС будет работать почти исключительно на нужды Постгрес.
Ну, это общая тенденция, все СУБД стараются перейти на самостоятельное управление дисковым вводом-выводом. Пользоваться ОС удобно, но она универсальна и не может учитывать всех особенностей именно баз данных. Но чтобы от нее отказаться — надо много сил положить.
А конкретных цифр я не скажу, не знаю.
Интересное подозрение возникло при чтении особенностей вытеснения.
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, конечно, будут вытеснены, но горячие буферы имеют шанс сохраниться.
WAL в PostgreSQL: 1. Буферный кеш