Понякога PostgreSQL бази данни трябва да импортират големи количества данни в един или минимален брой стъпки. Това е широко известно като групово импортиране на данни, където източникът на данни обикновено е един или повече големи файлове. Този процес понякога може да бъде неприемливо бавен.
Има много причини за такава лоша производителност:индекси, тригери, външни ключове, първични ключове на GUID или дори дневника за предсрочно записване (WAL) могат да причинят забавяне.
В тази статия ще разгледаме някои съвети за най-добри практики за групово импортиране на данни в PostgreSQL бази данни. Въпреки това, може да има ситуации, в които нито един от тези съвети няма да бъде ефективно решение. Препоръчваме на читателите да обмислят плюсовете и минусите на всеки метод, преди да го приложат.
Съвет 1:Променете целевата таблица на режим без регистрация
За PostgreSQL 9.5 и по-нова версия, целевата таблица може първо да бъде променена на UNLOGGED, след което да бъде променена обратно на LOGGED, след като данните се заредят:
ALTER TABLE <target table> SET UNLOGGED
<bulk data insert operations…>
ALTER TABLE <target table> LOGGED
Режимът UNLOGGED гарантира, че PostgreSQL не изпраща операции за запис на таблица към дневника за предсрочно записване (WAL). Това може да направи процеса на зареждане значително по-бърз. Въпреки това, тъй като операциите не се регистрират, данните не могат да бъдат възстановени, ако има срив или нечисто изключване на сървъра по време на натоварването. PostgreSQL автоматично ще съкрати всяка нерегистрирана таблица, след като се рестартира.
Също така, нерегистрираните таблици не се репликират на сървъри в режим на готовност. В такива случаи съществуващите репликации трябва да бъдат премахнати преди натоварването и да се създадат отново след натоварването. В зависимост от обема данни в първичния възел и броя на готовността, времето за повторно създаване на репликация може да е доста дълго и да не е приемливо от изискванията за висока наличност.
Препоръчваме следните най-добри практики за групово вмъкване на данни в нерегистрирани таблици:
- Създаване на резервно копие на таблицата и данните, преди да ги промените в режим без регистрация
- Пресъздаване на всяка репликация към сървъри в режим на готовност, след като зареждането на данни приключи
- Използване на нерегистрирани групови вмъквания за таблици, които могат лесно да бъдат повторно попълнени (напр. големи справочни таблици или таблици с измерения)
Съвет 2:Пуснете и създайте отново индекси
Съществуващите индекси могат да причинят значителни забавяния по време на групови вмъквания на данни. Това е така, защото при добавянето на всеки ред трябва да се актуализира и съответния запис в индекса.
Препоръчваме да изпуснете индексите в целевата таблица, където е възможно, преди да започнете груповото вмъкване, и да създадете отново индексите, след като зареждането приключи. Отново, създаването на индекси върху големи таблици може да отнеме време, но като цяло ще бъде по-бързо от актуализирането на индексите по време на зареждане.
DROP INDEX <index_name1>, <index_name2> … <index_name_n>
<bulk data insert operations…>
CREATE INDEX <index_name> ON <target_table>(column1, …,column n)
Може да си струва временно да увеличите maintenance_work_mem конфигурационен параметър точно преди създаването на индексите. Увеличената работна памет може да помогне за по-бързото създаване на индексите.
Друг вариант да играете на сигурно е да направите копие на целевата таблица в същата база данни със съществуващи данни и индекси. След това тази новокопирана таблица може да бъде тествана с групово вмъкване и за двата сценария:пускане и повторно създаване на индекси или динамично актуализиране. Методът, който дава по-добро представяне, може да бъде последван за масата на живо.
Съвет 3:Пуснете и създайте отново чужди ключове
Подобно на индексите, ограниченията на външния ключ също могат да повлияят на производителността на групово натоварване. Това е така, защото всеки външен ключ във всеки вмъкнат ред трябва да бъде проверен за съществуването на съответен първичен ключ. Зад кулисите, PostgreSQL използва тригер за извършване на проверката. Когато зареждате голям брой редове, този тригер трябва да бъде задействан за всеки ред, като се добавят допълнителните разходи.
Освен ако не е ограничено от бизнес правилата, препоръчваме да премахнете всички външни ключове от целевата таблица, да заредите данните в една транзакция, след което да създадете отново външните ключове след извършване на транзакцията.
ALTER TABLE <target_table>
DROP CONSTRAINT <foreign_key_constraint>
BEGIN TRANSACTION
<bulk data insert operations…>
COMMIT
ALTER TABLE <target_table>
ADD CONSTRAINT <foreign key constraint>
FOREIGN KEY (<foreign_key_field>)
REFERENCES <parent_table>(<primary key field>)...
Още веднъж, увеличаване на maintenance_work_mem конфигурационният параметър може да подобри производителността на пресъздаването на ограничения на външния ключ.
Съвет 4:Деактивирайте тригерите
Тригерите INSERT или DELETE (ако процесът на зареждане включва и изтриване на записи от целевата таблица) могат да причинят забавяне в груповото зареждане на данни. Това е така, защото всеки тригер ще има логика, която трябва да бъде проверена, и операции, които трябва да завършат непосредствено след вмъкването или изтриването на всеки ред.
Препоръчваме да деактивирате всички задействания в целевата таблица преди групово зареждане на данни и да ги активирате след като зареждането приключи. Деактивирането на ВСИЧКИ задействания включва и системни тригери, които налагат проверки на ограниченията за външния ключ.
ALTER TABLE <target table> DISABLE TRIGGER ALL
<bulk data insert operations…>
ALTER TABLE <target table> ENABLE TRIGGER ALL
Съвет 5:Използвайте командата COPY
Препоръчваме да използвате PostgreSQL COPY команда за зареждане на данни от един или повече файлове. COPY е оптимизиран за групово зареждане на данни. Това е по-ефективно от изпълнението на голям брой изрази INSERT или дори многозначни INSERT.
COPY <target table> [( column1>, … , <column_n>)]
FROM '<file_name_and_path>'
WITH (<option1>, <option2>, … , <option_n>)
Други предимства от използването на COPY включват:
- Поддържа както текст, така и импортиране на двоични файлове
- Той е от транзакционен характер
- Позволява уточняване на структурата на входните файлове
- Може условно да зарежда данни с помощта на клауза WHERE
Съвет 6:Използвайте многозначно INSERT
Изпълнението на няколко хиляди или няколко стотици хиляди оператори INSERT може да бъде лош избор за групово натоварване на данни. Това е така, защото всяка отделна команда INSERT трябва да бъде анализирана и подготвена от оптимизатора на заявки, да премине през всички проверки на ограниченията, да се изпълни като отделна транзакция и да влезе в WAL. Използването на единичен оператор INSERT с много стойности може да спести тези допълнителни разходи.
INSERT INTO <target_table> (<column1>, <column2>, …, <column_n>)
VALUES
(<value a>, <value b>, …, <value x>),
(<value 1>, <value 2>, …, <value n>),
(<value A>, <value B>, …, <value Z>),
(<value i>, <value ii>, …, <value L>),
...
Многозначната производителност на INSERT се влияе от съществуващите индекси. Препоръчваме да изпуснете индексите преди да изпълните командата и да ги създадете отново след това.
Друга област, която трябва да знаете, е количеството памет, налична за PostgreSQL за изпълнение на многозначни INSERT. Когато се изпълнява INSERT с много стойности, голям брой входни стойности трябва да се поберат в RAM и освен ако няма достатъчно налична памет, процесът може да се провали.
Препоръчваме да зададете effective_cache_size параметър на 50% и shared_buffer параметър до 25% от общата RAM памет на машината. Освен това, за да бъде в безопасност, той изпълнява серия от многозначни INSERT, като всеки израз има стойности за 1000 реда.
Съвет 7:Стартирайте ANALYZE
Това не е свързано с подобряване на ефективността на груповото импортиране на данни, но силно препоръчваме да стартирате ANALYZE команда на целевата таблица непосредствено след насипния внос. Голям брой нови редове значително ще изкривят разпределението на данните в колони и ще доведат до неактуалност на всички съществуващи статистически данни в таблицата. Когато оптимизаторът на заявки използва остарели статистически данни, производителността на заявката може да бъде неприемливо ниска. Изпълнението на командата ANALYZE ще гарантира, че всички съществуващи статистически данни са актуализирани.
Последни мисли
Груповото импортиране на данни може да не се случва всеки ден за приложение за база данни, но има въздействие върху производителността върху заявките, когато се изпълнява. Ето защо е необходимо да се сведе до минимум времето за зареждане възможно най-добре. Едно нещо, което DBA могат да направят, за да сведат до минимум всяка изненада, е да тестват оптимизацията на натоварването в среда за разработка или етапна среда със сходни спецификации на сървъра и конфигурации на PostgreSQL. Всеки сценарий за зареждане на данни е различен и най-добре е да изпробвате всеки метод и да намерите този, който работи.