Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Пропоную ознайомитися з розшифровкою доповіді початку 2016 року Андрія Сальникова "Типові помилки у додатках, які ведуть до bloat у postgresql"

У цій доповіді я розберу основні помилки у програмах, які виникають на етапі проектування та написання коду програми. І візьму тільки ті помилки, які ведуть до bloat у Postgresql. Як правило, це початок кінця продуктивності вашої системи в цілому, хоча спочатку ніяких передумов до цього не було видно.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Радий усіх вітати! Ця доповідь не така технічна як попередня від мого колеги. Ця доповідь орієнтована на розробників бекенд-систем в основному, тому що у нас досить велика кількість клієнтів. І всі вони роблять одні й самі помилки. Про них я розповім вам. Поясню до чого фатального та поганого ведуть ці помилки.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Чому роблять помилки? Здійснюються вони з двох причин: на авось, може так прокотить і від незнання якихось механізмів, що відбуваються на рівні між базою та додатком, а також у самій базі.

Я вам наведу три приклади із жахливими картинками того, як все стало погано. Коротко розповім про механізм, що там відбувається. І як з ними боротися, коли вони трапилися, і які превентивні методи використовуватиме для запобігання помилкам. Розповім про допоміжні інструменти і дам корисні посилання.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Я використав тестову базу даних, де мав дві таблички. Одна табличка з рахунками клієнтів, інша з операціями з цих рахунків. І з певною періодичністю ми оновлюємо залишки на цих рахунках.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Вихідні дані таблички: вона досить маленька, 2 MB. Час відповіді за основою і саме по табличці теж дуже непоганий. І досить хороше навантаження - 2 операцій на секунду по табличці.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

І крізь цю доповідь я вам показуватиму графіки, щоб наочно було зрозуміло, що відбувається. Завжди буде 2 слайди з графіками. Перший слайд – це те, що відбувається на сервері.

І в цій ситуації ми бачимо, що справді у нас табличка невеликого розміру. Індекс невеликий 2 MB. Це перший ліворуч графік.

Середній час відповіді на сервері теж стабільний, невеликий. Це правий верхній графік.

Лівий нижній графік – це найтриваліші транзакції. Ми бачимо, що транзакції швидко виконуються. І автовакуум тут ще не працює, тому що це був старт-тест. Далі він працюватиме і буде корисним нам.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Другий слайд завжди буде присвячений випробуваній табличці. У цій ситуації ми постійно оновлюємо залишки за рахунками у клієнта. І ми бачимо те, що середній час відповіді щодо операції оновлення досить хороший, менше мілісекунди. Бачимо, що ресурси процесора (це правий верхній графік) споживаються також рівномірно і досить невеликі.

Правий нижній графік показує, скільки пам'яті операційної та дискової ми перебираємо у пошуках нашого потрібного рядка, перш ніж його оновити. І кількість операція по табличці - 2 в секунду, як і спочатку я говорив.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

І тепер у нас стається трагедія. З якоїсь причини з'являється довга забута транзакція. Причини зазвичай усі банальні:

  • Одна з найпоширеніших - те, що ми в коді програми почали звертатися до зовнішнього сервісу. І це сервіс нам не відповідає. Т. е. ми відкрили транзакцію, зробили зміну в базі і пішли з програми пошту почитати або до іншого сервісу в рамках нашої інфраструктури, і він з якоїсь причини нам не відповідає. І в нас повисла сесія у стані – невідомо, коли вирішиться.
  • Друга ситуація, коли у нас у коді з якоїсь причини стався висновок. І ми у винятку не обробили закриття транзакції. І у нас вийшла сесія з відритою транзакцією.
  • І останній – це також досить частий випадок. Це неякісний код. Деякі фреймворки відкривають транзакцію. Вона висить, і ви можете не знати у додатку, що вона у вас висить.

До чого ведуть такі речі?

До того, що в нас починають різко роздмухуватися таблиці та індекси. Це саме цей ефект bloat. Для бази це висловлюватиметься у цьому, що ми дуже різко збільшиться час відповіді бази даних, збільшиться навантаження сервер бази даних. І як результат у нас страждатиме додаток. Тому що якщо ви в коді витрачали 10 мілісекунд на запит до бази, 10 мілісекунд на свою логіку, то у вас функція відпрацьовувала 20 мілісекунд. А зараз у вас ситуація буде дуже сумною.

І давайте подивимося, що відбувається. Лівий нижній графік показує, що в нас довга довга транзакція. І якщо ми подивимося на лівий верхній графік, то бачимо, що розмір таблиці з двох мегабайт у нас різко стрибнув у 300 мегабайт. При цьому кількість даних у таблиці не змінилася, тобто там досить багато сміття.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Загальна ситуація за середнім часом відповіді сервера також змінилася кілька порядків. Т. е. всі запити по серверу почали капітально просідати. І при цьому запустилися внутрішні процеси Postgres в особі автовакууму, які намагаються щось робити і споживають ресурси.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Що ж із нашою табличкою відбувається? Теж саме. Середній час відповіді на табличці у нас стрибнув на кілька порядків вгору. Якщо саме з споживаних ресурсів, ми бачимо, що дуже збільшилася навантаження на процесор. Це правий верхній графік. І збільшилася вона тому, що процесору доводиться перебирати купу марних рядків у пошуках однієї потрібної. Це правий нижній графік. І як результат – кількість викликів за секунду у нас почала просаджуватися дуже сильно, тому що база не встигає обробляти ту саму кількість запитів.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Нам треба повертатись до життя. Ліземо в інтернет і дізнаємося, що довгі транзакції призводять до проблеми. Знаходимо та вбиваємо цю транзакцію. І у нас все стає нормально. Все працює як слід.

Ми заспокоїлися, але через деякий час починаємо помічати, що програма працює не так, як до аварійної ситуації. Запити обробляються однаково повільніше, причому значно повільніше. У півтора-два рази повільніше у моєму прикладі. Навантаження на сервер теж вище, ніж було до аварії.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

І питання: "Що відбувається з базою в цей момент?". А з базою відбувається така ситуація. На графіку транзакцій ви бачите, що вона зупинилася і там справді немає тривалих транзакцій. Але розміри таблички під час аварії у нас фатально зросли. І з того часу не зменшилися. Середній час за базою стабілізувався. І відповіді начебто ходять адекватно із прийнятною для нас швидкістю. Автовакуум став активнішим і почав щось робити з табличкою, тому що йому потрібно перелопачувати більшу кількість даних.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Саме по випробуваній табличці з рахунками, де ми змінюємо залишки: час відповіді на запит як би повернулося норму. Але насправді воно у півтора рази вище.

І за навантаженням на процесор ми бачимо, що навантаження на процесор не повернулося в потрібну величину до аварії. І причини там криються якраз у правому нижньому графіці. Видно, що там відбувається перебір якоїсь кількості пам'яті. Т. е. для пошуку потрібного рядка ми витрачаємо ресурси сервера база даних при перебиранні марних даних. Кількість транзакцій за секунду стабілізувалася.

Загалом добре, але ситуація гірша, ніж була. Явна деградація бази даних як наслідок нашої програми, яка працює з цією базою даних.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

І щоб розібратися, що там відбувається, якщо ви не були на попередній доповіді, то зараз трішки теорії. Теорія про процес внутрішній. Навіщо автовакуум та що він робить?

Буквально коротко для розуміння. У якийсь час ми маємо таблицю. У таблиці у нас є рядки. Ці рядки можуть бути активними, живими, потрібними нам зараз. На малюнку вони позначені зеленим кольором. І є рядки мертві, які вже відпрацювали, були оновлені, з'явилися нові записи. І вони позначені, що вони вже не цікаві базою даних. Але лежать у таблиці через особливості Postgres.

Навіщо потрібний автовакуум? Автовакуум в якийсь момент приходить, звертається до бази даних і запитує її: «Дай мені, будь ласка, id найстарішої транзакції, яка відкрита на даний момент у базі даних». База даних повертає цей ID. І автовакуум, спираючись на нього, перебирає рядки в таблиці. І якщо бачить, що якісь рядки були змінені транзакціями набагато старішими, то він має право їх позначити як рядки, які ми можемо перевикористовувати в майбутньому, записавши туди нові дані. Це фоновий процес.

Ми в цей час продовжуємо працювати з базою даних, продовжуємо робити якісь зміни у таблиці. І на ці рядки, які ми можемо перевикористовувати, записуємо нові дані. І таким чином у нас виходить кругообіг, тобто весь час там виникають якісь мертві старі рядки, натомість ми записуємо нові рядки, які нам потрібні. І це нормальний стан для роботи PostgreSQL.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Що сталося під час аварії? Як там відбувався цей процес?

Ми мали табличку в якомусь стані, якісь живі, якісь мертві рядки. Прийшов автовакуум. Він запитав базу даних про те, яка у нас найдавніша транзакція, яка у неї id. Отримав цей id, який може бути багатогодинною давністю, може десятихвилинною. Це залежить від того, наскільки сильне навантаження у вас у базі даних. І пішов шукати рядки, які він може помітити як ті, що перевикористовуються. І не знайшов таких рядків у нашій таблиці.

Але в цей час продовжуємо працювати з таблицею. Щось робимо у ній, оновлюємо, змінюємо дані. А що базі даних у цей час робити? Їй нічого не залишається, як дописувати нові рядки наприкінці існуючої таблиці. І тим самим у нас розмір таблиці починає роздмухуватися.

Реально нам для роботи потрібні зелені рядки. Але під час такої проблеми виходить, що відсоток зелених рядків вкрай низький у всьому обсязі таблиці.

А коли ми виконуємо запит, базі даних доводиться пробігатися всіма рядками: і червоним, і зеленим, щоб знайти потрібний рядок. І ефект роздуття таблиці марними даними називається «bloat», який ще й жере наш дисковий простір. Пам'ятаєте, було 2 МБ, стало 300 МБ? А тепер поміняйте мегабайти на гігабайти і ви так досить швидко втратите всі запаси своїх дискових ресурсів.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Які наслідки можуть бути для нас?

  • У моєму прикладі таблиця та індекс зросли у 150 разів. У деяких наших клієнтів бували фатальніші випадки, коли просто місце на диску починалося закінчуватися.
  • Розмір таблиць сам собою ніколи не зменшиться. Автовакуум у деяких випадках може відрізати хвостик таблиці, якщо там лише мертві рядки. Але оскільки відбувається постійна ротація, один зелений рядок може в кінці зависнути і не оновлюватися, а решта десь на початку таблички записуватимуться. Але це настільки малоймовірна подія, що у вас сама собою таблиця зменшиться в розмірах, що не варто на це сподіватися.
  • Базі даних необхідно перебирати весь стос марних рядків. І ми витрачаємо дискові ресурси, витрачаємо ресурси процесора та електроенергію.
  • І це безпосередньо впливає на нашу програму, тому що якщо на початку ми витрачали 10 мілісекунд на запит, 10 мілісекунд на наш код, то під час аварії ми стали витрачати секунду на запит і 10 мілісекунд на код, тобто на порядок продуктивність програми знизилася. І коли дозволили аварію у нас стало витрачається 20 мілісекунд на запит, 10 мілісекунд на код. Це означає, що ми все одно просіли в півтора рази за продуктивністю. І це все через одну транзакцію, яка підвисла, причому, можливо, з нашої вини.
  • І питання: «Як все повернути назад?», щоб у нас стало все добре, і запити забігали так само швидко, як до аварії.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

І тому є певний цикл робіт, який проводиться.

Спочатку нам необхідно знайти проблемні таблиці, що роздулися. Ми розуміємо, що за якимись таблицями запис іде активніше, за якимись менш активно. І для цього використовується розширення pgstattuple. Встановивши це розширення, ви можете написати запити, які допоможуть вам знайти таблиці, які роздулися досить сильно.

Після того, як ви знайшли ці таблиці, їх потрібно стиснути. Для цього вже є інструменти. У нашій компанії ми використовуємо три інструменти. Перший – вбудований VACUUM FULL. Він жорстокий, суворий і нещадний, але іноді дуже корисний. Pg_repack и pgcompacttable – це сторонні утиліти для стиснення таблиць. І вони більш дбайливо ставляться до бази даних.

Вони використовуються в залежності від того, що вам зручніше. Але про це я розповім наприкінці. Головне, що є три інструменти. Є з чого вибрати.

Після того, як ми всі поправили, переконалися, що все стало добре, ми повинні знати, як запобігти цій ситуації в майбутньому:

  • Запобігається вона досить легко. Потрібно стежити за тривалістю сесій на Майстер-сервері. Особливо небезпечні сесії у стані idle in transaction. Це ті, які якраз відкрили транзакцію, щось зробили та пішли чи просто повисли, загубилися у коді.
  • І для вас, як для розробників, важливо тестувати код на момент виникнення цих ситуацій. Це легко зробити. Це буде корисна перевірка. Ви уникнете велику кількість «дитячих» проблем, пов'язаних із тривалими транзакціями.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

На цих графіках я хотів показати, як змінилася табличка і поведінка бази даних після того, як я пройшов у даному випадку VACUUM FULL'ом по табличці. Це я не production.

Розмір таблиці повернувся відразу до нормального робочого стану в пару мегабайт. На середній час відповіді на сервері це не сильно вплинуло.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Але саме по нашій випробуваній табличці, де ми оновлювали залишки на рахунках, бачимо, що середній час відповіді на запит оновлення даних у табличці скоротилося до доаварійного рівня. Ресурси, споживані процесором виконання цього запиту, теж впали до доаварійного рівня. І правий нижній графік показує, що зараз ми знаходимо рівно той рядок, який нам потрібний відразу, не перебираючи скоп мертвих рядків, які були до стиснення таблиці. І середній час запитів приблизно на тому рівні залишився. Але тут у мене швидше похибка мого заліза.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

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

Історія друга, в якій ми розподіляємо навантаження та оптимізуємо серверні ресурси

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

  • Ми вже виросли та стали серйозними хлопцями. І розуміємо, що ми маємо репліку і добре б нам збалансувати навантаження: писати на Майстер, а з репліки читати. І зазвичай ця ситуація виникає, коли ми хочемо готувати якісь звіти чи ETL. І бізнес цьому дуже радіє. Він дуже хоче різноманітних звітів із купою аналітики складною.
  • Звіти багатогодинні, бо складну аналітику не порахуєш за мілісекунди. Ми, як браві хлопці, пишемо код. Робимо у додатку вставки, що запис ми ведемо на Майстер, звіти виконуємо на репліці.
  • Розподіляємо навантаження.
  • Все працює чудово. Ви молодці.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

І як ця ситуація виглядає? Саме на цих графіках ще для тривалості транзакції додав тривалість транзакцій з репліки. Всі інші графіки відносяться лише до Майстер-сервера.

Табличка зі звітами на цей момент у мене підросла. Їх побільшало. Ми бачимо, що середній час відповіді сервера є стабільним. Ми бачимо, що на репліці ми маємо тривалу транзакцію, яка працює 2 години. Бачимо спокійну роботу автовакууму, який обробляє мертві рядки. І все у нас добре.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Саме по випробуваній табличці ми продовжуємо там оновлювати залишки на рахунках. І у нас теж стабільний час відповіді на запит, стабільне споживання ресурсів. Все добре.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Все добре доти, доки у нас ці звіти не починають відстрілюватися з конфлікту з реплікацією. І вони відстрілюються з постійною періодичністю.

Ми ліземо в інтернет і починаємо читати, чому це відбувається. І знаходимо рішення.

Перше рішення – збільшити затримку реплікації. Ми знаємо, що у нас звіт працює 3 години. Ставимо затримку реплікації – 3:XNUMX. Запускаємо все, але у нас все одно продовжуються проблеми з тим, що звіти іноді відстрілюються.

Ми хочемо, щоб у нас все було ідеально. Лезем далі. І знаходимо в інтернеті класне налаштування – hot_standby_feedback. Вмикаємо його. Hot_standby_feedback дозволяє нам притримати роботу автовакууму на Майстері. Тим самим ми зовсім позбавляємося конфліктів реплікації. І нас усе працює добре зі звітами.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

А що тим часом у нас відбувається з Майстер-сервером? А з Майстер-сервером у нас відбувається тотальне лихо. Зараз ми спостерігаємо графіки, коли я увімкнув обидві ці налаштування. І ми бачимо, що сесія на репліці в нас якимось чином почала впливати на ситуацію на Майстер-сервері. Вона справді впливає, тому що вона призупинила автовакуум, який вичищає мертві рядки. У нас розмір таблиці знову стрибнув у небо. Середній час виконання запитів по всій базі даних теж стрибнув у небо. Автовакууми трохи напружилися.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Саме по нашій табличці ми бачимо, що по ній оновлення даних теж стрибнуло в небо. Споживання ресурсів процесора аналогічно дуже збільшилося. Ми знову перебираємо велику кількість мертвих марних рядків. І час відповіді на цій табличці, кількість транзакцій впала.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Як це буде мати вигляд, якщо ми не знаємо, про що я говорив до цього?

  • Ми починаємо шукати проблеми. Якщо ми стикалися з проблемами в першій частині, ми знаємо, що це може бути причина у довгій транзакції та ліземо на Майстер. Проблема у нас на Майстері. Ковбасить його. Він гріється, має Load Average під сотню.
  • Запити там гальмують, але ми там не бачимо жодних тривалих транзакцій. І не розуміємо, у чому річ. Не розуміємо де шукати.
  • Перевіряємо серверне обладнання. Можливо у нас розвалився raid. Може, у нас згоріла планка пам'яті. Та будь що може бути. Але ні, сервери нові, все працює чудово.
  • Бігають усі: адміністратори, розробники та директор. Нічого не допомагає.
  • І в якийсь момент все несподівано починає виправлятися.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

На репліці в наш час запит відпрацював і пішов. Ми отримали звіт. Бізнес досі задоволений. Як бачимо, табличка у нас зросла знову і не збирається зменшуватись. На графіку із сесіями я залишив шматочок ось цієї довгої транзакції з репліки, щоб ви могли оцінити, наскільки тривалий час минає, доки ситуація стабілізується.

Сесія пішла. І лише через якийсь час сервер приходить більш-менш у порядок. І середній час відповіді за запитами на Майстер-сервері приходить у норму. Тому що, нарешті, автовакуум отримав можливість вичищати, помічати ці мертві рядки. І він почав виконувати свою роботу. І наскільки швидко він її робить, настільки швидко ми прийдемо до ладу.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

По випробуваній табличці, де ми оновлюємо залишки за рахунками, ми бачимо таку саму картину. Середній час оновлення рахунку також нормалізується поступово. Ресурси, які споживаються процесором, теж зменшуються. І кількість транзакцій за секунду повертається до норми. Але знову в норму не таку, як була у нас до аварії.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Ми в будь-якому випадку отримуємо просідання за продуктивністю як і в першому випадку в півтора-два рази, а іноді й більше.

Ми начебто зробили все правильно. Розподіл навантаження. Обладнання не простоює. Розумом розбили запити, але все одно все погано вийшло.

  • Чи не включати hot_standby_feedback? Так, його без особливо сильних причин не рекомендується вмикати. Тому що ця крутила безпосередньо впливає на Майстер-сервер і зупиняє роботу автовакууму там. Увімкнувши його на якійсь репліці та забувши про це, ви можете убити Майстер і отримати великі проблеми з програмою.
  • Збільшувати max_standby_streaming_delay? Так, для звітів це так. Якщо у вас тригодинний звіт і ви не хочете, щоб він у вас падав через конфлікти реплікацій, просто збільште затримку. Тривалий звіт ніколи не вимагає даних, які прийшли до бази прямо зараз. Якщо він у вас тригодинний, то ви його запускаєте за якийсь старий період даних. І вам, що три години затримки, що шість годин затримки – жодної ролі не зіграє, зате ви стабільно отримуватимете звіти і не знатимете проблем із падінням їх.
  • Звичайно, потрібно контролювати тривалі сесії на репліках, особливо, якщо ви вирішили включити hot_standby_feedback на репліці. Тому що може бути будь-що. Дали цю репліку розробнику, щоб він протестував запити. Він написав божевільний запит. Запустив і пішов пити чай, а ми отримали Майстер, що склався. Або ми туди пустили не те додаток. Ситуації різноманітні. Сесії на репліках необхідно контролювати так само, як і на Майстері.
  • І якщо у вас є швидкі та тривалі запити щодо реплік, то в даному випадку краще для розподілу навантаження розбити їх. Це посилання до streaming_delay. Для швидких мати одну репліку із невеликою затримкою реплікації. Для тривалих звітних запитів мати репліку, яка може відставати на 6 годин, на добу. Це цілком нормальна ситуація.

Усуваємо наслідки тим самим способом:

  • Знаходимо роздуті таблиці.
  • І стискаємо найзручнішим інструментом, який нам підходить.

Історія друга у цьому завершилася. Переходимо до історії третьої.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

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

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

  • Будь-який програмний продукт зростає. Змінюються щодо нього вимоги. Ми у будь-якому разі хочемо розвиватися. І буває так, що нам необхідно оновити дані у таблиці, саме прогнати апдейт у плані нашої міграції під новий функціонал, який ми впроваджуємо у рамках нашого розвитку.
  • Старий формат даних не влаштовує. Припустимо, ми зараз звернемося до другої таблички, де в мене операції з цих рахунків. І, скажімо, вони були в рублях, а ми вирішили підвищити точність і робити в копійках. І для цього нам потрібно зробити апдейт: поле із сумою операції помножити на сто.
  • У світі ми використовуємо автоматизовані засоби контролю версій бази даних. Припустимо, Liquibase. Прописуємо туди нашу міграцію. Тестуємо її на нашій тестовій базі. Все відмінно. Апдейт минає. Блокує роботу на деякий час, але ми отримуємо оновлені дані. І можемо запускати новий функціонал у цьому. Усі відтестували, перевірили. Усі підтвердили.
  • Здійснили планові роботи, провели міграцію.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Ось міграція із апдейтом представлена ​​перед вами. Так як це у мене операції по рахунках, табличка була 15 GB. І оскільки ми оновлюємо кожен рядок, ми апдейтом роздмухали табличку в два рази, тому що ми перезаписали кожен рядок.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Під час міграції ми нічого не могли робити з цією табличкою, тому що всі запити до неї стали в чергу і чекали, доки закінчиться цей апдейт. Але тут я хочу звернути вашу увагу на цифри на вертикальній осі. Т. е. ми маємо середній час запиту до міграції в районі 5 мілісекунд і навантаження на процесор, кількість блокових операцій з читання пам'яті диска менше, ніж у 7,5.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Провели міграцію та здобули знову проблеми.

Міграція пройшла успішно, але:

  • Старий функціонал почав виконуватися довше.
  • Таблиця знову зросла у розмірах.
  • Навантаження на сервер знову стало більше, ніж було.
  • І, природно, ми поки що поралися з тим функціоналом, який працював добре, ми його трошки покращили.

І це знову bloat, який нам знову псує життя.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Тут я демонструю, що таблиця, як і в попередніх двох випадках, не збирається повертатися до попередніх розмірів. Середнє навантаження по серверу начебто адекватне.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

А якщо ми звернемося до таблиці з рахунками, то побачимо, що середній час запиту у нас зріс у два рази до цієї табличці. Навантаження на процесор і кількість рядків, що перебираються в пам'яті, стрибнуло вище 7,5, а було нижче. І стрибнуло у разі процесорів у 2 рази, у разі блокових операцій у 1,5 раза, тобто ми отримали деградацію продуктивності сервера. І як наслідок – деградацію продуктивності нашої програми. При цьому кількість дзвінків залишилася приблизно на тому ж рівні.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

І тут головне розуміти, як правильно робити такі міграції. А їх треба робити. Ми постійно робимо ці міграції.

  • Такі великі міграції не роблять автоматично. Вони завжди мають бути підконтрольними.
  • Необхідний контроль з боку знаючої людини. Якщо у вас є DBA у команді, то нехай це робить DBA. Це його робота. Якщо ні, то найдосвідченіша людина нехай це робить, яка знає, як працювати з базами даних.
  • Нова схема бази даних, навіть якщо ми оновлюємо один стовпець, ми завжди готуємо етапами, т. е. заздалегідь до того, як викотиться нова версія програми:
  • Додаються нові поля, в які будемо записувати якраз оновлені дані.
  • Переносимо дані зі старого поля до нового поля невеликими частинами. Чому ми це робимо? По-перше, ми завжди контролюємо процес цього процесу. Ми знаємо, що ми перенесли вже стільки батчів і нам залишилося стільки.
  • А другий позитивний ефект у тому, що між кожним таким батчем ми закриваємо транзакцію, відкриваємо нову і це дає можливість автовакууму відпрацювати по табличці, помітити мертві рядки до перевикористання.
  • Для рядків, які будуть з'являтися в процесі роботи програми (у нас ще працює стара програма) додаємо тригер, який записує нові значення в нові поля. У нашому випадку – це помноження на XNUMX старого значення.
  • Якщо ми зовсім уперті і хочемо те саме поле, то після завершення всіх міграцій і перед накатом нової версії програми, ми просто перейменовуємо поля. Старі в якусь придуману назву, а нові поля перейменовуємо на старі.
  • І лише після цього запускаємо нову версію програми.

І при цьому ми не отримаємо bloat і не просядемо за продуктивністю.

На цьому третя історія закінчилась.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat_approx.sql

І зараз трохи більш докладно про інструменти, які я згадував у першій історії.

До того як шукати bloat, потрібно обов'язково поставити розширення pgstattuple.

Щоб вам не вигадувати запити, ми вже написали ці запити. Ви можете використовувати їх. Тут представлено два запити.

  • Перший досить довго працює, зате він вам покаже точні значення bloat по таблиці.
  • Другий працює швидше і дуже ефективний, коли потрібно швидко оцінити – чи є bloat чи ні bloat за таблицею. І ще ви повинні розуміти, що bloat у таблиці Postgres є завжди. Це особливість моделі MVCC.
  • І 20% bloat - це нормально для таблиць у більшості випадків. Т. е. вам не варто переживати і стискати цю таблицю.

Як виявляти таблиці, які у нас розпухли, ми розібралися, причому коли розпухли марними даними.

Тепер про те, як виправляти bloat:

  • Якщо у нас невелика табличка і хороші диски, тобто на табличці до гігабайта можна використовувати VACUUM FULL. Візьме він у вас блокування на ексклюзивну таблицю на кілька секунд і добре, зате швидко і жорстко все зробить. Що робить VACUUM FULL? Він бере ексклюзивне блокування на таблицю і зі старих таблиць переписує живі рядки до нової таблиці. І насамкінець підміняє їх місцями. Старі файли видаляє, нові підставляє замість старих. Але на час своєї роботи він бере ексклюзивне блокування таблиці. Це означає, що ви з цією таблицею нічого не зможете зробити: ні писати до неї, ні читати до неї, ні модифікувати її. І VACUUM FULL вимагає додаткового місця на диску для запису даних.
  • Наступний інструмент pg_repack. За своїм принципом він дуже схожий на VACUUM FULL, тому що він теж переписує дані зі старих файлів у нові та замінює їх у таблиці. Але при цьому не бере ексклюзивне блокування на таблицю самого початку своєї роботи, а бере тільки в момент, коли у нього вже готові дані для того, щоб підмінити файлики. Вимоги щодо дискових ресурсів у нього аналогічні як VACUUM FULL. Вам потрібне додаткове місце на диску, а це іноді буває критично, якщо у вас є терабайтні таблиці. І він досить ненажерливий процесором, тому що веде активну роботу з введенням-висновком.
  • Третя утиліта – це pgcompacttable. Вона більш дбайливо ставиться до ресурсів, тому що працює трохи за іншими принципами. Основна суть pgcompacttable в тому, що вона апдейтами в таблиці переносить всі живі рядки на початок таблиці. І потім запускає вакуум за цією таблицею, тому що ми знаємо, що у нас на початку живі, а наприкінці мертві рядки. І вакуум вже сам відрізає цей хвостик, тобто додаткового дискового простору він не потребує. І при цьому його ще можна за ресурсами стискати.

Із інструментами все.

Типові помилки в додатках, які ведуть до bloat в postgresql. Андрій Сальников

Якщо вам тема з bloat здасться цікавою в плані покопатися далі всередину, то вам деякі корисні посилання:

  • https://www.slideshare.net/alexius2Mb/where-is-the-space-postgres - Це доповідь мого колеги. Він загальний про те, куди подіється місце у Postgres у процесі його роботи та життя. І там дуже великий і докладний технічний шматок для адміністраторів баз даних про bloat.
  • https://github.com/dataegret/pg-utils – це посилання наш репозиторій, де ми зберігаємо купу корисних скриптів на перевірку стану бази даних. Там ви можете знайти скрипти з пошуку bloat.
  • третя и четверта посилання на інструменти, які допоможуть вам утискати таблички.
  • http://blog.dataegret.com/2Mb018/03/postgresql-bloatbusters.html – це пост мого колеги. Там він досить серйозно та докладно технічно розбирає bloat саме вже на рівні близькому до адміністраторів.

Я тут більше постарався показати страшилку для девелоперів, тому що вони є безпосередніми нашими клієнтами баз даних і повинні розуміти, до чого та які дії ведуть. Сподіваюся, у мене це вийшло. Дякую за увагу!

Питання

Дякую за доповідь! Ви говорили, як можна виявляти проблеми. А як їх можна попереджати? Т. е. у мене була ситуація, коли запити висіли не тільки через те, що вони зверталися до якихось зовнішніх сервісів. Це були просто якісь дикі joins. Були якісь малесенькі запити невинні, які добу висіли, а потім починали творити якусь дурницю. Т. е. дуже схоже на те, що ви описуєте. Як це слідкувати? Сидіти та постійно дивитися, який запит завис? Як це можна попередити?

В даному випадку це завдання для адміністраторів вашої компанії, необов'язково для DBA.

Я адміністратор.

У PostgreSQL є таке уявлення, як pg_stat_activity, в якому показані запити, що висять. І ви можете побачити, як довго він там висить.

Я мушу кожні 5 хвилин заходити та дивитися?

Налаштуйте cron та перевіряйте. Якщо у вас виник тривалий запит, пишіть листа та все. Т. е. вам не потрібно очима дивитися, це можна автоматизувати. Вам прийде листа, ви на нього реагуєте. А можете автоматично відстрілювати.

Чи є явні причини, чому це відбувається?

Я дехто перерахував. Інші складніші приклади. І там розмова надовго може бути.

Дякую за доповідь! Про утиліту pg_repack хотів уточнити. Якщо воно не робить ексклюзивне блокування, то…

Вона робить ексклюзивне блокування.

... то я потенційно можу втратити дані. Моя програма нічого не повинна записувати в цей час?

Ні, воно спокійно працює з таблицею, тобто pg_repack переносить спочатку всі живі рядки, які є. Звісно, ​​там якийсь запис до таблиці відбувається. Він просто цей хвостик докидає.

Т. е. він наприкінці все-таки робить?

В кінці він бере ексклюзивне блокування на те, щоб замінювати місцями ці файли.

Це буде швидше ніж VACUUM FULL?

VACUUM FULL, як стартанув, одразу взяв ексклюзивне блокування. І доки він усе не зробить, він її не відпустить. pg_repack бере ексклюзивне блокування тільки на момент заміни файлів. У цей момент ви туди не запишіть, але дані не загубляться, все буде гаразд.

Вітаю! Ви розповідали про роботу автовакууму. Там був графік з червоними, жовтими та зеленими осередками запису. Т. е. жовті - він помітив як віддалені. І тому в них можна щось записати нове?

Так. Postgres не видаляє рядки. Має таку специфіку. Якщо ми оновили рядок, ми помітили стару як віддалену. Там встає id транзакції, який змінив цей рядок, і записуємо новий рядок. І ми маємо сесії, які потенційно можуть їх читати. Якоїсь миті вони вже зовсім старими стають. І суть роботи автовакууму в тому, що він пробігається цими рядками і позначає їх як непотрібні. І туди можна перезаписати дані.

Я зрозумів. Але питання трохи не про це. Я не домовився. Припустимо, що ми маємо таблицю. У ній є поля змінного розміру. І якщо я спробую щось вставити нове, то це може в старий осередок просто не влізти.

Ні, там у будь-якому випадку весь рядок оновлюється. Postgres має дві моделі зберігання даних. Він вибирає від типу даних. Є дані, які зберігаються безпосередньо в таблиці, а є ще tos-дані. Це величезні обсяги даних: текст, json. Вони зберігаються в окремих табличках. І за цими табличками відбувається та ж історія з bloat, тобто все те ж саме. Просто окремо їх винесено.

Дякую за доповідь! Наскільки прийнятно використовуватиме обмеження тривалості запити statement timeout?

Дуже прийнятно. Ми скрізь це використовуємо. І тому що у нас своїх сервісів немає, ми надаємо віддалену підтримку, то досить різноманітні клієнти є. І всіх цілком задовольняє це. Т. е. у нас є завдання в cron, які перевіряють. Просто із клієнтом обговорюється тривалість сесій, раніше якої ми не прибиваємо. Це може бути хвилина, може бути 10 хвилин. Це залежить від навантаження на базу та її цілі. Але всі ми використовуємо pg_stat_activity.

Дякую за доповідь! Намагаюся приміряти вашу доповідь до своїх додатків. І начебто ми всюди стартуємо транзакцію, скрізь явно її завершуємо. Якщо якийсь висновок, то все одно rollback відбувається. І тут я замислився. Адже транзакція може стартанути не явно. Це підказка дівчині, мабуть. Якщо я просто роблю оновлення запису, транзакція стартане в PostgreSQL і завершиться лише тоді, коли відбудеться відключення з'єднання?

Якщо ви говорите зараз про рівень програми, це залежить від того драйвера, який ви використовуєте, від того ORM, який використовується. Там дуже багато налаштувань. Якщо у вас включений auto commit on, то там стартує транзакція, відразу закривається.

Т. е. закривається вона відразу після апдейта?

Це залежить від налаштувань. Одне налаштування я назвав. Це auto commit on. Вона досить поширена. Якщо вона включена, то відкрилася-закрилася транзакція. Якщо ви явно не сказали "start transaction" та "end transaction", а просто запустили в сесію запит.

Вітаю! Дякую за доповідь! Припустимо, що у нас є база, яка пухне-пухне і тут на сервері закінчується місце. Чи є якісь інструменти, щоб виправити цю ситуацію?

Місце на сервері потрібно моніторити.

Наприклад, DBA пішов пити чай, був на курорті тощо.

Коли створюється файлова система, там як мінімум якесь резервне місце створюється, куди не пишуться дані.

А якщо зовсім під нуль?

Там так і називається reserved space, тобто його можна звільнити і залежно від того, наскільки великим його створили, ви отримали вільне місце. За промовчанням я не знаю, скільки там. А в іншому випадку – доставляти диски, щоб у вас було місце провести відновлювальну операцію. Можна видалити якусь таблицю, яка вам гарантовано не потрібна.

Інших інструментів нема?

Це завжди ручна робота. І за місцем виявляється, що там краще зробити, бо є критичні дані, є некритичні. І для кожної бази та програми, яка з нею працює, це залежить від бізнесу. Завжди за місцем вирішується.

Дякую за доповідь! У мене два запитання. По-перше, ви демонстрували слайди, де показувалося, що у випадку транзакцій, що зависли, зростає як і обсяг табличного простору, так і розмір індексу. І далі за доповіддю була купа утиліт, які пакують табличку. А що з індексом?

Вони теж пакують їх.

Але вакуум не торкається індексу?

Деякі працюють із індексом. Наприклад, pg_rapack, pgcompacttable. Вакуум перетворює індекси, торкається їх. У VACUUM FULL суть у тому, щоби все перезаписати, тобто він з усіма працює.

І друге питання. Я не зрозумів, чому звіти на репліках так сильно залежать від реплікації. Мені здавалося, що звіти – це читання, а реплікація – запис.

У чому виникає конфлікт реплікації? Ми маємо Майстер, на якому відбуваються процеси. У нас відбувається автовакуум. Автовакуум за фактом, що робить? Він випилює якісь старі рядки. Якщо у нас у цей час на репліці йде запит, який читає ці старі рядки, а на Майстрі сталася ситуація, що автовакуум помітив ці рядки як можливі для перезапису, ми їх перезаписали. І у нас прийшов пакет даних, коли ми повинні перезаписати ті рядки, які потрібні запиту на репліці, то процес реплікації зачекає на той тайм аут, який ви налаштували. І потім PostgreSQL вирішуватиме, що важливіше для нього. А реплікація для нього важливіша за запит і він відстрілить запит, щоб виконати ці зміни на репліці.

Андрію, є питання. Ось ці чудові графіки, які ви показували під час презентації, це результат робіт якоїсь вашої утиліти? Чим будували графіки?

Це сервіс Okmeter.

Чи це комерційний продукт?

Так. Це – комерційний продукт.

Джерело: habr.com

Додати коментар або відгук