Чому ОНОВЛЕННЯ Postgres тривало 39 годин?


17

У мене є таблиця Postgres з ~ 2,1 млн рядків. Я запустив оновлення нижче:

WITH stops AS (
    SELECT id,
           rank() OVER (ORDER BY offense_timestamp,
                     defendant_dl,
                     offense_street_number,
                     offense_street_name) AS stop
    FROM   consistent.master
    WHERE  citing_jurisdiction=1
)

UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;

Цей запит зайняв 39 годин. Я працюю цим на 4 (фізичному) процесорі ноутбука i7 Q720 для ноутбука, багато оперативної пам’яті, більше нічого не працює переважна більшість часу. Немає місця на жорсткому диску. Нещодавно таблицю було вакуумовано, проаналізовано та перероблено.

Весь час роботи запиту, принаймні після початкового WITHзавершення, використання процесора зазвичай було низьким, а жорсткий диск використовувався 100%. HDD використовувався настільки важко, що будь-який інший додаток працював значно повільніше, ніж зазвичай.

Налаштування живлення ноутбука були на високому рівні (Windows 7 x64).

Ось ПОЯСНЕННЯ:

Update on master  (cost=822243.22..1021456.89 rows=2060910 width=312)
  CTE stops
    ->  WindowAgg  (cost=529826.95..581349.70 rows=2060910 width=33)
          ->  Sort  (cost=529826.95..534979.23 rows=2060910 width=33)
                Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
                ->  Seq Scan on master  (cost=0.00..144630.06 rows=2060910 width=33)
                      Filter: (citing_jurisdiction = 1)
  ->  Hash Join  (cost=240893.51..440107.19 rows=2060910 width=312)
        Hash Cond: (stops.id = consistent.master.id)
        ->  CTE Scan on stops  (cost=0.00..41218.20 rows=2060910 width=48)
        ->  Hash  (cost=139413.45..139413.45 rows=2086645 width=268)
              ->  Seq Scan on master  (cost=0.00..139413.45 rows=2086645 width=268)

citing_jurisdiction=1виключає лише кілька десятків тисяч рядків. Навіть з цим WHEREпунктом я все ще працюю на понад 2 мільйони рядків.

Жорсткий диск повністю зашифрований приводом TrueCrypt 7.1a. Те , що уповільнює трохи, але не досить , щоб викликати запит , щоб прийняти , що багато годин.

Час WITHзаймає лише близько 3 хвилин.

У arrest_idполі не було індексу для зовнішнього ключа. У цій таблиці є 8 індексів та 2 зовнішніх ключа. Усі інші поля запиту індексуються.

arrest_idПоле не було ніяких обмежень , крім NOT NULL.

У таблиці налічується 32 стовпці.

arrest_idмає різний тип характеру (20) . Я усвідомлюю, що rank()створює числове значення, але мені потрібно використовувати символи, що змінюються (20), оскільки у мене є інші рядки, де citing_jurisdiction<>1для цього поля використовуються нечислові дані.

arrest_idПоле було порожнім для всіх рядків з citing_jurisdiction=1.

Це персональний ноутбук високого класу (станом на 1 рік тому). Я єдиний користувач. Інші запити чи операції не виконувалися. Блокування здається малоймовірним.

Ні в цій таблиці, ні в іншому місці бази даних немає тригерів.

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


Це Seq Scanтрохи страшно ...
rogerdpack

Відповіді:


18

Нещодавно у мене трапилось щось подібне зі столом у 3,5 мільйона рядків. Моє оновлення ніколи не закінчиться. Після багатьох експериментів і розчарувань я нарешті знайшов винуватця. Це виявились індекси в таблиці, що оновлюється.

Рішенням було скинути всі індекси на оновлену таблицю перед запуском оператора оновлення. Як тільки я це зробив, оновлення закінчилося за кілька хвилин. Після завершення оновлення я знову створив індекси та повернувся до справи. Напевно, це вам не допоможе, але, можливо, хтось ще шукатиме відповіді.

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


3
Я переключаю найкращу відповідь на вас. Оскільки я опублікував це, я стикався з іншими ситуаціями, коли індекси є проблемою, навіть якщо стовпець, який оновлюється, вже має значення і не має індексу (!). Схоже, у Postgres є проблема з тим, як вона управляє індексами в інших стовпцях. Немає жодних причин, щоб ці інші індекси відображали час запиту оновлення, коли єдиною зміною таблиці є оновлення невкладеного стовпця, і ви не збільшуєте виділений простір для жодного рядка цього стовпця.
Арен Камбре

1
Спасибі! Сподіваюся, це допомагає іншим. Це врятувало б мені години головного болю для чогось, здавалося б, дуже простого.
JC Avena

5
@ArenCambre - є причина: PostgreSQL копіює весь рядок в інше місце і стару версію позначає як видалену. Ось як PostgreSQL реалізує багатоверсійний контроль сумісності (MVCC).
Piotr Findeisen

Моє запитання ... чому він винуватець? Дивіться також stackoverflow.com/a/35660593/32453
rogerdpack

15

Ваша найбільша проблема - це велика кількість важкої роботи на жорсткому диску ноутбука. Це ніколи не буде швидким, незалежно від того, що ви робите, особливо якщо це такий тип повільнішого накопичувача 5400 об / хв, що постачається на безлічі ноутбуків.

TrueCrypt сповільнює роботу записів більше, ніж "трохи". Читання буде досить швидким, але записи дозволяють RAID 5 виглядати швидко. Запуск БД на томі TrueCrypt буде тортурами для записів, особливо випадкових.

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

BEGIN;
SELECT ... INTO TEMPORARY TABLE master_tmp ;
TRUNCATE TABLE consistent.master;
-- Now DROP all constraints on consistent.master, then:
INSERT INTO consistent.master SELECT * FROM master_tmp;
-- ... and re-create any constraints.

Я підозрюю, що це буде швидше, ніж просто скинути і відтворити обмеження поодинці, тому що в UPDATE будуть досить випадкові шаблони запису, які вб'ють ваше сховище. Дві масові вставки, одна в незаблоковану таблицю і одна в таблицю з журналом WAL без обмежень, ймовірно, буде швидшою.

Якщо у вас абсолютно актуальні резервні копії, і ви не заперечуєте відновити базу даних із резервних копій, ви також можете перезапустити PostgreSQL з fsync=offпараметром і full_page_writes=off тимчасово для цієї масової операції. Будь-яка несподівана проблема, як втрата електроенергії або збої в ОС, залишить вашу базу даних невиправною fsync=off.

POSTGreSQL, еквівалентний "без реєстрації", полягає у використанні невідкритих таблиць. Ці незаблоковані таблиці обрізаються, якщо БД закривається нечисто, поки вони брудні. Використання незаблокованих таблиць принаймні вдвічі зменшить завантаження вашої записи та зменшить кількість запитів, тож вони можуть бути НАШОМО швидшими.

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


Ця відповідь помітна на великій кількості записів та жахливій шифрі шифрування плюс повільному приводі ноутбука. Я хотів би також відзначити , що наявність 8 індексів дає багато додаткових операцій записи і поразок застосовності HOT в-блоці оновлень рядків, тому видалення індексів і використовуючи нижній коефіцієнт заповнення на столі може запобігти тонну міграції ряду
dbenhur

1
Хороший заклик збільшити шанси HOTS за допомогою фунфактора - хоча з TrueCrypt примушуючи блокувати цикли читання-перезапису у величезних блоках, я не впевнений, що це дуже допоможе; міграція рядків може бути навіть більш швидкою, оскільки вироблення таблиці принаймні робить лінійні блоки записів.
Крейг Рінгер

Через 2,5 роки я роблю щось подібне, але на більшому столі. Просто для того, щоб переконатися, чи корисно опустити всі індекси, навіть якщо одинарний стовпець, який я оновлюю, не індексується?
Арен Камбре

1
@ArenCambre У цьому випадку ... ну, це складно. Якщо більшість ваших оновлень буде придатною, HOTтоді краще залишити індекси на місці. Якщо ні, то, швидше за все, ви захочете відмовитись та створити її знову. Стовпець не індексується, але для того, щоб зробити оновлення HOT, там також повинно бути вільне місце на одній сторінці, тому трохи залежить від того, скільки мертвих місць є в таблиці. Якщо це в основному письмо, я скажу всі індекси. Якщо оновлено багато, у ньому можуть бути отвори, і ви, можливо, все в порядку. Інструменти на кшталт pageinspectі pg_freespacemapможуть допомогти визначити це.
Крейг Рінгер

Спасибі. У цьому випадку це булева колонка, яка вже мала запис у кожному рядку. Я змінював запис у деяких рядках. Я щойно підтвердив: оновлення зайняло лише 2 години після скидання всіх індексів. Заздалегідь мені довелося зупинити оновлення через 18 годин, оскільки це зайняло занадто довго. Це незважаючи на те, що стовпець, який оновлювався, точно не індексувався.
Арен Камбре

2

Хтось дасть кращу відповідь на Postgres, але ось декілька спостережень з точки зору Oracle, які можуть застосовуватися (а коментарі занадто довгі для поля коментарів).

Першим моїм питанням було б намагання оновити 2 мільйони рядків за одну транзакцію. В Oracle ви б писали попереднє зображення кожного оновлюваного блоку, щоб інші сеанси все-таки послідовно читали, не читаючи змінених блоків, і ви мали змогу відкатати. Це довгий відкат, який будується. Зазвичай вам краще робити дрібні шматки. Скажіть 1000 записів одночасно.

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

Oracle дозволяє "не вести журнал" натяки на заяви, щоб зупинити проїзд. Це прискорює багато тверджень, але залишає ваш db у ситуації, що не може бути відновлена. Таким чином, ви хочете зробити резервну копію раніше, а потім знову зробити резервну копію. Я не знаю, чи є у Postgres подібні варіанти.


PostgreSQL не має проблем з тривалим відкатом, не існує. ROLBACK дуже швидко працює в PostgreSQL, незалежно від того, наскільки велика ваша транзакція. Oracle! = PostgreSQL
Frank Heikens

@FrankHeikens Спасибі, що цікаво. Мені доведеться прочитати про те, як працюють мандрівки на Postgres. Для того, щоб вся концепція транзакцій працювала, потрібно якось підтримувати дві різні версії даних під час транзакції, зображення перед зображенням і зображення після цього, і це механізм, про який я маю на увазі. Так чи інакше, я б припустив, що існує поріг, за який ресурси для підтримки транзакції будуть занадто дорогими.
Гленн

2
@Glenn postgres зберігає версії рядка в самій таблиці - див. Тут для пояснення. Компроміс полягає в тому, що у вас висять «мертві» кортежі, які очищуються асинхронно з тим, що називається «вакуумом» у постгресах (Oracle не потребує вакууму, оскільки він ніколи не має «мертвих» рядків у самій таблиці)
каже Джек спробуйте topanswers.xyz

Запрошуємо вас і досить запізніло: ласкаво просимо на сайт :-)
Джек каже спробувати topanswers.xyz

@Glenn Канонічний документ для контролю паралельної версії PostgreSQL для рядкової версії - postgresql.org/docs/current/static/mvcc-intro.html і його варто прочитати. Дивіться також wiki.postgresql.org/wiki/MVCC . Зауважте, що MVCC з мертвими рядами і VACUUMє лише половиною відповіді; PostgreSQL також використовує так званий "журнал запису вперед" (фактично журнал) для забезпечення атомних комірок та захисту від часткових записів тощо. Див. Postgresql.org/docs/current/static/wal-intro.html
Крейг Рінгер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.