Wydajność funkcji

46

Wychodząc z tła MySQL, gdzie wydajność procedury przechowywanej (starszy artykuł) i użyteczność są wątpliwe, oceniam PostgreSQL pod kątem nowego produktu dla mojej firmy.

Jedną z rzeczy, które chciałbym zrobić, jest przeniesienie logiki aplikacji do procedur przechowywanych, więc tutaj proszę o DOs i DON'T (najlepsze praktyki) dotyczące korzystania z funkcji w PostgreSQL (9.0), szczególnie w odniesieniu do pułapek wydajności.

Derek Downey
źródło
masz na myśli, że nie chcesz, aby odpowiedzi wspominały o czymkolwiek niezwiązanym z wydajnością?
Jack Douglas,
Chris Travers dużo pisze na blogach o zaletach korzystania z procedur przechowywanych, np. Tutaj: ledgersmbdev.blogspot.de/2012/07/... i tutaj: ledgersmbdev.blogspot.de/2012/07/... po prostu przejrzyj swój blog, są wiele interesujących artykułów na ten temat.
a_horse_w_no_name

Odpowiedzi:

51

Ściśle mówiąc, termin „procedury przechowywane” wskazuje na procedury SQL w Postgres, wprowadzone w Postgres 11. Powiązane:

Są też funkcje , które działają prawie, ale nie do końca tak samo, i były tam od samego początku.

Funkcje z LANGUAGE sqlsą w zasadzie tylko plikami wsadowymi z prostymi poleceniami SQL w opakowaniu funkcji (a zatem atomowym, zawsze uruchamianym w ramach pojedynczej transakcji) akceptującym parametry. Wszystkie instrukcje w funkcji SQL są planowane jednocześnie , co nieznacznie różni się od wykonywania jednej instrukcji po drugiej i może wpływać na kolejność wykonywania blokad.

Co więcej, najbardziej dojrzałym językiem jest PL / pgSQL ( LANGUAGE plpgsql). Działa dobrze i był ulepszany z każdym wydaniem w ciągu ostatniej dekady, ale najlepiej służy jako klej do poleceń SQL. Nie jest przeznaczony do ciężkich obliczeń (innych niż polecenia SQL).

Funkcje PL / pgSQL wykonują zapytania takie jak przygotowane instrukcje . Ponowne użycie buforowanych planów zapytań zmniejsza część kosztów związanych z planowaniem i czyni je nieco szybszymi niż równoważne instrukcje SQL, co może być zauważalnym efektem w zależności od okoliczności. Może mieć również skutki uboczne, jak w tym powiązanym pytaniu:

Niesie to zalety i wady przygotowanych wypowiedzi - jak omówiono w instrukcji . W przypadku zapytań o tabele z nieregularnym rozkładem danych i zmiennymi parametrami dynamiczny SQL z EXECUTEmoże działać lepiej, gdy zysk ze zoptymalizowanego planu wykonania dla danego parametru (parametrów) przewyższa koszt ponownego planowania.

Ponieważ ogólne plany wykonania Postgres 9.2 są nadal buforowane dla sesji, ale cytując instrukcję :

Odbywa się to natychmiast dla przygotowanych instrukcji bez parametrów; w przeciwnym razie ma to miejsce dopiero po wyprodukowaniu pięciu lub więcej wykonań planów, których szacunkowy średni koszt (w tym koszty ogólne planowania) jest droższy niż ogólny kosztorys planu.

Przez większość czasu uzyskujemy to, co najlepsze z obu światów (pomniejszone o dodatkowe koszty ogólne) bez użycia (ab) EXECUTE. Szczegóły w Co nowego w PostgreSQL 9.2 na PostgreSQL Wiki .

Postgres 12 wprowadza dodatkową zmienną serwera,plan_cache_mode aby wymusić plany ogólne lub niestandardowe. W szczególnych przypadkach należy zachować ostrożność.

Możesz wygrać duże dzięki funkcjom po stronie serwera, które zapobiegają dodatkowym objazdom do serwera bazy danych z Twojej aplikacji. Niech serwer wykona jak najwięcej zadań jednocześnie i zwróci tylko dobrze określony wynik.

Unikaj zagnieżdżania złożonych funkcji, zwłaszcza funkcji tabel ( RETURNING SETOF recordlub TABLE (...)). Funkcje to czarne skrzynki, które stanowią bariery optymalizacyjne w narzędziu do planowania zapytań. Są one optymalizowane osobno, nie w kontekście zewnętrznego zapytania, co ułatwia planowanie, ale może skutkować mniej niż doskonałymi planami. Ponadto kosztów i wielkości wyników funkcji nie można wiarygodnie przewidzieć.

Wyjątkiem od tej reguły są proste funkcje SQL ( LANGUAGE sql), które mogą być „inline” - jeśli zostaną spełnione pewne warunki . Przeczytaj więcej o tym, jak działa narzędzie do planowania zapytań w tej prezentacji autorstwa Neila Conwaya (zaawansowane rzeczy).

W PostgreSQL funkcja zawsze działa automatycznie w ramach jednej transakcji . Wszystko się udaje lub nic. Jeśli wystąpi wyjątek, wszystko jest wycofywane. Ale jest obsługa błędów ...

Dlatego też funkcje nie są dokładnie „procedurami składowanymi” (nawet jeśli termin ten jest czasem wprowadzany w błąd). Niektóre polecenia podoba VACUUM, CREATE INDEX CONCURRENTLYczy CREATE DATABASEnie można uruchomić wewnątrz bloku transakcji, więc nie są one dozwolone w funkcji. (Ani w procedurach SQL, jak na Postgres 11. To może zostać dodane później.)

Przez lata napisałem tysiące funkcji plpgsql.

Erwin Brandstetter
źródło
2
@nhahtdh: „automatyczna transakcja” nie jest terminem technicznym. To był po prostu mało elegancki sposób powiedzenia ... co teraz mówi po moim wyjaśnieniu. W ogóle nie jest to autonomiczna transakcja. „autonomiczny” to po prostu podobne słowo.
Erwin Brandstetter,
4
Twoje odpowiedzi opracowane tutaj i SO mogą być epickim podręcznikiem najlepszych praktyk PostGreSQL.
Davos,
10

Niektóre DO:

  • W miarę możliwości używaj SQL jako języka funkcji, ponieważ PG może wstawiać instrukcje
  • Używaj IMMUTABLE / STABLE / VOLATILE poprawnie, ponieważ PG może buforować wyniki, jeśli jest niezmienne lub stabilne
  • Użyj STRICT poprawnie, ponieważ PG może po prostu zwrócić null, jeśli jakiekolwiek dane wejściowe są null zamiast uruchamiać funkcję
  • Rozważ PL / V8, gdy nie możesz użyć SQL jako języka funkcji. Jest szybszy niż PL / pgSQL w niektórych nienaukowych testach, które przeprowadziłem
  • Użyj LISTEN / NOTIFY do dłuższych procesów, które mogą wystąpić poza transakcją
  • Rozważ użycie funkcji do wdrożenia paginacji, ponieważ paginacja oparta na kluczach może być szybsza niż paginacja oparta na LIMIT
  • Upewnij się, że przetestowałeś swoje funkcje
Neil McGuigan
źródło
Po raz pierwszy widzę twierdzenie, że PL / V8 jest szybszy niż PL / pgSQL. Czy masz jakieś (opublikowane) dane na poparcie tego?
a_horse_w_no_name
@ a_horse_w_no_name nie, nie mam. Tak jak powiedziałem, zrobiłem kilka nienaukowych testów. Były to głównie logika, a nie dostęp do danych. Spróbuję wykonać kilka powtarzalnych testów przed świętami Bożego Narodzenia i ponownie opublikować tutaj.
Neil McGuigan,
@ a_horse_with_no_name oto szybki i brudny przykład dla FizzBuzz plv8 vs plpgsql: blog.databasepatterns.com/2014/08/plv8-vs-plpgsql.html
Neil McGuigan
8

Mówiąc ogólnie, przeniesienie logiki aplikacji do bazy danych oznacza, że ​​jest szybsza - w końcu będzie działać bliżej danych.

Uważam (ale nie jestem w 100% pewien), że funkcje języka SQL są szybsze niż te używające innych języków, ponieważ nie wymagają przełączania kontekstu. Minusem jest to, że logika proceduralna nie jest dozwolona.

PL / pgSQL jest najbardziej dojrzałym i kompletnym z wbudowanych języków - ale w celu zwiększenia wydajności można użyć C (choć przyniesie to tylko korzyści obliczeniowe)

Jack Douglas
źródło
7

Możesz zrobić kilka bardzo interesujących rzeczy za pomocą funkcji zdefiniowanych przez użytkownika (UDF) w postgresql. Na przykład możesz użyć dziesiątek możliwych języków. Wbudowane pl / sql i pl / pgsql są zarówno zdolne, jak i niezawodne i wykorzystują metodę piaskownicy, aby powstrzymać użytkowników od robienia czegokolwiek zbyt strasznie niebezpiecznego. UDF napisane w C zapewniają najwyższą moc i wydajność, ponieważ działają w tym samym kontekście co sama baza danych. To jednak jak zabawa z ogniem, ponieważ nawet małe błędy mogą powodować ogromne problemy, awarie backendów lub dane. Języki custome pl, takie jak pl / R, pl / ruby, pl / perl itd. Zapewniają możliwość pisania zarówno warstw bazy danych, jak i aplikacji w tych samych językach. Może to być przydatne, ponieważ oznacza to, że nie musisz uczyć java programisty perla lub pl / pgsql itp., Aby pisać UDF.

Na koniec jest język pl / proxy . Ten język UDF umożliwia uruchamianie aplikacji na kilkudziesięciu lub więcej serwerach postgresql zaplecza w celu skalowania. Został opracowany przez dobrych ludzi ze Skype i zasadniczo pozwala na rozwiązanie skalowania poziomego biednego człowieka. Zaskakująco łatwo jest też pisać.

A teraz kwestia wydajności. To jest szary obszar. Czy piszesz aplikację dla jednej osoby? A może za 1000? lub za 10 000 000? Sposób, w jaki zbudujesz swoją aplikację i użyjesz UDF, zależeć będzie WIELE od tego, jak próbujesz skalować. Jeśli piszesz dla tysięcy użytkowników, główną rzeczą, którą chcesz zrobić, jest jak najbardziej zmniejszyć obciążenie bazy danych. UDF, które zmniejszają ilość danych przenoszonych z powrotem do bazy danych, pomogą zmniejszyć obciążenie IO. Jeśli jednak zaczną zwiększać obciążenie procesora, mogą stanowić problem. Ogólnie mówiąc, priorytetem jest zmniejszenie obciążenia IO, a następnie upewnienie się, że UDF są wydajne, aby nie przeciążać procesorów.

Scott Marlowe
źródło