Додавання нульового стовпчика до таблиці коштує більше 10 хвилин


11

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

ALTER TABLE mytable ADD mycolumn VARCHAR(50);

Корисна інформація:

  • Версія PostgreSQL: 9.1
  • Кількість рядків: ~ 250К
  • Кількість стовпців: 38
  • Кількість нульових стовпців: 32
  • Кількість обмежень: 5 (1 ПК, 3 ФК, 1 УНІКАЛЬНИЙ)
  • Кількість покажчиків: 1
  • Тип ОС: Debian Squeeze 64

Я знайшов цікаву інформацію про те, як PostgreSQL управляє нульовими стовпцями (через HeapTupleHeader).

Перший мій припущення полягає в тому, що оскільки в цій таблиці вже є 32 нульові стовпчики з 8- MAXALIGNбітними розмірами, HeapTupleHeader має довжину 4 байти (не перевірено, і я не знаю, як це зробити).

Отже, додаючи новий нульовий стовпець, можливо, знадобиться оновлення HeapTupleHeader у кожному рядку, щоб додати нові 8-бітні MAXALIGN, що може спричинити проблеми з продуктивністю.

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

ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;

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

Чи маєте ви уявлення про те, що може спричинити вартість цієї вистави?


1
Ну, я можу сказати вам частину його: Змінення типу стовпця на інший тип, який не є бінарним сумісним, насправді створює новий стовпець, копіює дані та встановлює старий стовпець як скинутий. Однак, SET NOT NULLце не змінює тип, воно лише додає обмеження - але обмеження потрібно перевірити проти таблиці, і це вимагає повного сканування таблиці. 9.4 покращує деякі з цих випадків, приймаючи слабкі замки, але він все ще досить важкий.
Крейг Рінгер

1
Перш ніж підозрювати, що вона працює повільно, вам потрібно переконатися, що ALTER TABLE не просто чекає блокування. Згадайте це у запитанні, якщо ви перевірили.
Даніель Верете

Дякую Крейгу та Даніелу. Коли я запускаю команду alter, вона з'являється в pg_stat_activity з очікуванням "true", я вважаю, що це означає, що вона чекає блокування !? Це хороший спосіб перевірки? До речі, перед запуском цього альтера все проходить нормально, але через кілька секунд після запуску кількість замків зростає

Спробуйте запит на wiki.postgresql.org/wiki/Lock_dependency_information для кращого перегляду. Або у вас затримані транзакції, які ви забули здійснити, або велика активність із цією таблицею, яка завжди зайнята.
Даніель Верете

Можливо, це краще підходить на dba.SE.
Erwin Brandstetter

Відповіді:


8

Тут є кілька непорозумінь:

Нуль растровий є НЕ частиною заголовка купи кортежу. За документацію:

Є заголовок фіксованого розміру (займає 23 байти на більшості машин), за ним слід необов'язковий нульовий растровий малюнок ...

Ваші 32 зведені стовпці не викликають сумнівів з двох причин:

  • Нульове растрове зображення додається на рядок , і лише якщо в рядку є хоча б одне фактичне NULLзначення . Зменшувані стовпці не мають прямого впливу, а лише фактичні NULLзначення. Якщо нульовий растровий файл виділений, він завжди виділяється повністю (все або нічого). Фактичний розмір нульової растрової карти - 1 біт на стовпець, округлений до наступного байта . По поточному коду souce:

    #define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
  • Нульова растрова карта виділяється після заголовка кучного пакета, а за ним - необов'язковий OID, а потім дані рядків Початок даних OID або рядків позначається t_hoffу заголовку. За вихідним кодом коментаря :

    Зауважте, що t_hoff має бути кратним MAXALIGN.

  • Є один вільний байт після заголовка кучного кортежу, який займає 23 байти. Таким чином, нульова растрова карта для рядків до 8 стовпців ефективно отримує без додаткових витрат. За допомогою 9-го стовпця таблиці t_hoffвисувається ще один MAXALIGN(як правило, 8) байт, щоб забезпечити ще 64 стовпці. Тож наступний кордон був би у 72 колонах.

Щоб відобразити інформацію управління кластером баз даних PostgreSQL (включаючи MAXALIGN), приклад типової установки Postgres 9.3 на машині Debian:

    sudo /usr/lib/postgresql/9.3/bin/pg_controldata /var/lib/postgresql/9.3/main

Я оновив інструкції у відповідній відповіді, яку ви цитували .

Все це вбік, навіть якщо ваш ALTER TABLEвипадок викликає перезапис цілої таблиці (що, мабуть, і є, змінюючи тип даних), 250K насправді не так вже й багато, і це було б за лічені секунди на будь-якій половині пристойної машини (якщо тільки рядки незвично великі) . 10 хвилин і більше свідчать про зовсім іншу проблему. Ваша заява чекає, щоб отримати замок на столі, швидше за все.

Зростаюча кількість записів pg_stat_activityозначає більш відкриті транзакції - вказує на паралельний доступ до столу (швидше за все), який повинен чекати завершення операції.

Кілька пострілів у темряві

Перевірте можливе роздуття столу, спробуйте ніжне VACUUM mytableабо більш агресивне VACUUM FULL mytable- що може зіткнутися з тими ж проблемами одночасності, оскільки ця форма також набуває ексклюзивний замок. Ви можете спробувати pg_repack замість ...

Я б почав з огляду можливих проблем з індексами, тригерами, зовнішнім ключем чи іншими обмеженнями, особливо тими, що стосуються стовпця. Особливо може бути задіяний пошкоджений індекс? Спробуйте REINDEX TABLE mytable;або DROPвсі їх та повторно додайте їх після ALTER TABLE тієї ж транзакції .

Спробуйте запустити команду вночі або коли не буде великого навантаження.

Методом грубої сили було б зупинити доступ до сервера, а потім спробувати ще раз:

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


2
Це майже напевно замки. Але, як тест, ви завжди можете створити копію таблиці і спробувати це змінити. Якщо це не займе багато часу, то ви знаєте, що проблема не в дійсній модифікації.

Дякую за пояснення Ервін. Я думаю, ти маєш рацію, здається, це проблема із замком. Коли я перевіряю pg_stat_activity, я можу побачити, що мій ALTER має значення "очікування". Що я не можу зрозуміти, це те, чому АЛЬТЕР не може отримати блокування на столі, бо навіть коли я не можу знайти жодного запиту, який працює, він, здається, не може його отримати. Але як тільки мій ALTER починає працювати, всі інші запити чекають його закінчення. Отже, діяльність, схоже, вказує на те, що ALTER блокує всі інші запити, а також вказує на те, що ALTER не отримав блокування. Я думаю, що є щось, що я не добре розумію !?

@MatthieuVerrecchia: Ви спробували тест, запропонований Річардом?
Ервін Брандштеттер

1
Я просто клонував свій стіл до нового (з pg_dump -> pg_sql). Новий стовпець правильно доданий через 50 мс, що підтверджує проблему блокування. До речі, досі не розумію, чому ALTER не може заблокувати дійсно стандартну db-активність.

1
@ErwinBrandstetter Я дотримувався ваших пропозицій і спробував VACUUM, потім REINDEX. REINDEX також блокувався, тому що він також не зміг придбати замок. Після деяких розслідувань проблема стала простішою, ніж ми, ти .. Залишився тиждень <IDLE> з відкритою транзакцією. Проблема вирішена, спасибі для всього, інформація була дуже корисною.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.