Pull to refresh
0
okmeter.io
Осмысленный мониторинг серверов и сайтов

Запись при чтении в postgresql: скандалы, интриги, расследования

Reading time 3 min
Views 25K

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


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


Под катом небольшой рассказ о неочевидном поведении postgresql.


SELECTы "пачкают" страницы


То есть SELECT вызывает модификацию каких-то записей, которые постгрес будет записывать на диск.




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


Все изменения в базе данных происходят в ходе транзакций, у каждой транзакции есть идентификационный номер txid (int32).


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



Картинка: www.interdb.jp


xmin — номер транзакции, которая создала этот tuple
xmax — номер транзакции, которая пометила этот tuple как удаленный


  • Если мы делаем INSERT в таблицу, он создает новый tuple (xmin=txid)
  • DELETE — помечает туплы, которые подходят под условие как удаленные (xmax=txid)
  • UPDATE делает условно DELETE + INSERT.

Когда мы выполняем SELECT, он помимо непосредственно поиска и выборки данных из таблицы делает еще и проверку видимости (visibility check).


Очень упрощенно, некоторая транзакця с номером txid1 "видит" данный тупл, если выполняется условия:


xmin < txid1 < xmax

Но изменения в кортежах происходят сразу, а транзакция может выполняться еще продолжительное время, поэтому в ходе проверки видимости необходимо удостовериться, завершились ли транзакции с номерами xmin, xmax и если да, то с каким статусом. Информацию о текущем состоянии каждой транзакции постгрес хранит в CLOG (commit log).


Так как проверять состояние большого количества транзакций в CLOG достаточно дорого по ресурсам, разработчики решили "закэшировать" эту информацию прямо в заголовке тупла. То есть когда какой-то SELECT видит к примеру, что xmin завершилась, он сохраняет это в так называемый hint bits — структуру поверх infomask, в которой записаны состояния транзакций xmin и xmax.


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


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


SELECT может вызывать синхронную запись на диск


Как известно, вся работа с данными в pg ведется через buffer cache, если нужных данных там нет, постгрес прочитает их с диска (c использованием OS page cache) и поместит в кэш.


При этом, если в кэше нет места, то из него вытесняется наименее востребованная страница. И наконец, если страница-кандидат на вытеснение оказывается "грязной", она должна быть записана на диск в тот же момент времени.


FrozenTransactionId


В начале статьи я упомянул, что счетчик транзакций в постгресе 32-битный, то есть он сбрасывается через каждые ~2 млрд транзакций.


Чтобы проверка видимости не превращалась в тыкву при сбросе счетчика транзакций, есть специальный процесс — wraparound vacuum.



До версии 9.4 этот процесс заменял xmin у тупла на специальное значение FrozenTransactionId=2. Транзакция с этим номером считалась старше любой другой транзакции. C 9.4 в тупл просто проставляется флаг, что xmin "заморожен", а сам xmin остается неизменным.


Для совсем внимательных: есть специальная константа BootstrapTransactionId=1, которая тоже старше всех других транзакций )


Итого


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


Пока ковырялся с постгресом нашел замечательную книгу "The Internals of PostgreSQL", рекомендую всем, кто не встречал ранее.

Tags:
Hubs:
+36
Comments 4
Comments Comments 4

Articles

Information

Website
okmeter.io
Registered
Employees
Unknown
Location
Россия