shopkeeper
tabela zawiera następujące pola:
id (bigint),amount (numeric(19,2)),createddate (timestamp)
Powiedzmy, że mam powyższą tabelę. Chcę uzyskać dane z wczoraj i wygenerować raport, drukując kwotę do centów.
Jednym ze sposobów jest wykonanie obliczeń w mojej aplikacji java i wykonanie prostego zapytania
Date previousDate ;// $1 calculate in application
Date todayDate;// $2 calculate in application
select amount where createddate between $1 and $2
a następnie przejrzyj rekordy i zamień kwoty na centy w mojej aplikacji java i wygeneruj raport
Innym sposobem jest wykonywanie obliczeń w samym zapytaniu sql:
select cast(amount * 100 as int) as "Cents"
from shopkeeper where createddate between date_trunc('day', now()) - interval '1 day' and date_trunc('day', now())
a następnie przejrzyj rekordy i wygeneruj raport
W jeden sposób całe moje przetwarzanie odbywa się w aplikacji java i uruchamiane jest proste zapytanie. W innym przypadku wszystkie konwersje i obliczenia są wykonywane w kwerendzie Sql.
Powyższy przypadek użycia jest tylko przykładem, w rzeczywistym scenariuszu tabela może mieć wiele kolumn, które wymagają przetwarzania podobnego rodzaju.
Czy możesz mi powiedzieć, które podejście jest lepsze pod względem wydajności i innych aspektów i dlaczego?
źródło
Odpowiedzi:
Zależy to od wielu czynników - ale przede wszystkim:
Jak zawsze, jeśli zrobić przynieść tył danych do aplikacji serwera, minimalizując kolumn i wierszy będą na swoją korzyść. Upewnienie się, że zapytanie jest dostrojone i odpowiednio zindeksowane, pomoże w obu przypadkach.
Uwaga:
Zapętlanie się przez rekordy jest prawie zawsze czymś złym w sql - preferowane jest pisanie operacji opartej na zbiorach.
Generalnie wolę ograniczyć pracę bazy danych do minimum „przechowuj te dane, pobierz te dane” - jednak zawsze są przykłady scenariuszy, w których eleganckie zapytanie na serwerze może zaoszczędzić dużo przepustowości.
Weź również pod uwagę: jeśli jest to kosztowne obliczeniowo, czy można je gdzieś buforować?
Jeśli chcesz dokładnego „co jest lepsze”; zakoduj go w obie strony i porównaj (zauważ, że pierwszy szkic jednego z nich prawdopodobnie nie jest dostrojony w 100%). Ale weź pod uwagę typowe użycie: jeśli w rzeczywistości jest wywoływane 5 razy (osobno) na raz, to zasymuluj to: nie porównuj tylko jednego „1 z nich do 1 z nich”.
źródło
Posłużę się metaforą: jeśli chcesz kupić złoty naszyjnik w Paryżu, złotnik mógłby usiąść w Kapsztadzie lub Paryżu, to kwestia umiejętności i gustu. Ale w tym celu nigdy nie wysłałbyś ton rudy złota z RPA do Francji. Ruda jest przetwarzana w miejscu wydobycia (lub przynajmniej w ogólnym obszarze), tylko złoto jest wysyłane. To samo powinno dotyczyć aplikacji i baz danych.
Jeśli chodzi o PostgreSQL , na serwerze można zrobić prawie wszystko, całkiem wydajnie. RDBMS doskonale sprawdza się przy złożonych zapytaniach. Dla potrzeb proceduralnych możesz wybierać spośród wielu języków skryptowych po stronie serwera : tcl, python, perl i wiele innych. Jednak głównie używam PL / pgSQL .
Najgorszym scenariuszem byłoby wielokrotne przechodzenie do serwera dla każdego wiersza większego zestawu. (To byłoby jak wysyłanie jednej tony rudy na raz.)
Po drugie , jeśli wysyłasz kaskadę zapytań, każde w zależności od poprzedniego, podczas gdy wszystko to można zrobić w jednym zapytaniu lub procedurze na serwerze. (To jak wysyłanie po kolei złota i każdego klejnotu na osobnym statku).
Przechodzenie między aplikacją a serwerem jest kosztowne. Dla serwera i klienta. Spróbuj ograniczyć ten problem, a wygrasz - ergo: w razie potrzeby użyj procedur po stronie serwera i / lub wyrafinowanego języka SQL.
Właśnie zakończyliśmy projekt, w którym prawie wszystkie złożone zapytania spakowaliśmy do funkcji Postgres. Aplikacja przekazuje parametry i pobiera potrzebne zbiory danych. Szybki, czysty, prosty (dla programisty aplikacji), I / O zredukowane do minimum ... błyszczący naszyjnik o niskim śladzie węglowym.
źródło
W takim przypadku prawdopodobnie lepiej wykonasz obliczenia w języku SQL, ponieważ silnik bazy danych prawdopodobnie będzie miał bardziej wydajne procedury arytmetyczne dziesiętne niż Java.
Generalnie jednak w przypadku obliczeń na poziomie wiersza nie ma dużej różnicy.
Gdzie ma to znaczenie, to:
źródło
Nie ma czarnego / białego określenia, które części logiki dostępu do danych powinny być wykonywane w SQL, a jakie części powinny być wykonywane w aplikacji. Podoba mi się sformułowanie Marka Gravella , rozróżnianie
Moc i wyrazistość SQL są mocno niedoceniane. Od czasu wprowadzenia funkcji okna , wiele obliczeń, które nie są ściśle zorientowane na zestawy, można wykonać bardzo łatwo i elegancko w bazie danych.
Niezależnie od ogólnej architektury aplikacji należy zawsze przestrzegać trzech praktycznych zasad:
Z mojego doświadczenia wynika, że mając przyzwoitą bazę danych i pewną przyzwoitą wiedzę na temat przyzwoitej bazy danych, szybko nie napotkasz limitów procesora tej bazy danych.
Dalsze lektury, w których wyjaśniono te rzeczy:
źródło
Ogólnie rzecz biorąc, rób rzeczy w SQL, jeśli są szanse, że również inne moduły lub komponenty w tym samym lub innych projektach będą musiały uzyskać te wyniki. atomowa operacja wykonana po stronie serwera jest również lepsza, ponieważ wystarczy wywołać przechowywany proces z dowolnego narzędzia do zarządzania bazą danych, aby uzyskać ostateczne wartości bez dalszego przetwarzania.
W niektórych przypadkach nie ma to zastosowania, ale jeśli tak jest, ma to sens. również ogólnie db box ma najlepszy sprzęt i wydajność.
źródło
Jeśli piszesz na ORM lub piszesz zwykłe aplikacje o niskiej wydajności, użyj dowolnego wzorca upraszczającego aplikację. Jeśli piszesz aplikację o wysokiej wydajności i dokładnie myślisz o skalowaniu, odniesiesz zwycięstwo, przenosząc przetwarzanie na dane. Zdecydowanie zalecam przeniesienie przetwarzania do danych.
Pomyślmy o tym w dwóch krokach: (1) transakcje OLTP (mała liczba rekordów). (2) OLAP (długie skany wielu rekordów).
W przypadku OLTP, jeśli chcesz być szybki (10 000 - 100 000 transakcji na sekundę), musisz usunąć rywalizację o zatrzask, blokadę i martwą blokadę z bazy danych. Oznacza to, że musisz wyeliminować długie przestoje w transakcjach: podróże w obie strony od klienta do DB w celu przeniesienia przetwarzania do klienta są jednym z takich długich przerw. Nie możesz mieć długotrwałych transakcji (aby uczynić atomowy odczyt / aktualizację) i mieć bardzo dużą przepustowość.
Re: skalowanie poziome. Nowoczesne bazy danych skalują się w poziomie. Systemy te już wdrażają HA i odporność na błędy. Wykorzystaj to i spróbuj uprościć przestrzeń aplikacji.
Spójrzmy na OLAP - w tym przypadku powinno być oczywiste, że przeciąganie ewentualnych terabajtów danych z powrotem do aplikacji to okropny pomysł. Systemy te są zbudowane specjalnie, aby działać niezwykle wydajnie w przypadku skompresowanych, wstępnie zorganizowanych danych kolumnowych. Nowoczesne systemy OLAP skalują się również w poziomie i mają wyrafinowane planery zapytań, które rozpraszają pracę w poziomie (wewnętrznie przenosząc przetwarzanie na dane).
źródło
To, czy wykonać obliczenia na froncie, czy na zapleczu, jest bardzo zdecydowane, czy możemy określić nasz cel we wdrożeniu biznesowym. W pewnym momencie kod Java może działać lepiej niż kod sql, zarówno dobrze napisany, jak i odwrotnie. Ale nadal, jeśli jesteś zdezorientowany, możesz spróbować najpierw ustalić -
Jest wiele innych aspektów, które możesz przemyśleć, zanim zdecydujesz, gdzie umieścić kod. Jedna opinia jest całkowicie błędna - wszystko można zrobić najlepiej w Javie (kod aplikacji) i / lub wszystko najlepiej zrobić za pomocą db (kod sql).
źródło
Z punktu widzenia wydajności: jest to bardzo prosta operacja arytmetyczna, którą prawie na pewno można wykonać znacznie szybciej niż faktyczne pobieranie danych z dysków, które stanowią podstawę bazy danych. Ponadto obliczanie wartości w klauzuli where prawdopodobnie będzie bardzo szybkie w dowolnym czasie wykonywania. Podsumowując, wąskim gardłem powinno być We / Wy dysku, a nie obliczanie wartości.
Jeśli chodzi o czytelność, myślę, że jeśli używasz ORM, powinieneś to zrobić w środowisku serwera aplikacji, ponieważ ORM pozwoli ci bardzo łatwo pracować z podstawowymi danymi, używając operacji opartych na zbiorach. Jeśli i tak zamierzasz napisać surowy SQL, nie ma nic złego w wykonywaniu tam obliczeń, Twój SQL również wyglądałby trochę ładniej i łatwiejszy do odczytania, gdyby był odpowiednio sformatowany.
źródło
Co najważniejsze, „wydajność” nie jest zdefiniowana.
Najbardziej liczy się dla mnie czas programisty.
Napisz zapytanie SQL. Jeśli jest zbyt wolny lub DB staje się wąskim gardłem, zastanów się ponownie. Do tego czasu będziesz w stanie porównać te dwa podejścia i podjąć decyzję na podstawie rzeczywistych danych dotyczących Twojej konfiguracji (sprzętu i dowolnego stosu, na którym się znajdujesz).
źródło
Nie sądzę, aby różnice w wydajności można było uzasadnić bez konkretnych przykładów i punktów odniesienia, ale mam inne podejście:
Które możesz utrzymać lepiej? Na przykład możesz chcieć przełączyć swój front-end z Java na Flash, HTML5, C ++ lub coś innego. Ogromna liczba programów przeszła taką zmianę, a nawet istnieje w więcej niż jednym języku, ponieważ muszą działać na wielu urządzeniach.
Nawet jeśli masz odpowiednią warstwę środkową (z podanego przykładu wydaje się, że tak nie jest), ta warstwa może się zmienić i JBoss może stać się Ruby / Rails.
Z drugiej strony jest mało prawdopodobne, że zastąpisz zaplecze SQL czymś, co nie jest relacyjną bazą danych SQL, a nawet jeśli to zrobisz, i tak będziesz musiał przepisać front-end od zera, więc kwestia jest dyskusyjna.
Mój pomysł jest taki, że jeśli wykonasz obliczenia w bazie danych, znacznie łatwiej będzie później napisać drugi front-end lub warstwę środkową, ponieważ nie musisz ponownie implementować wszystkiego. Jednak w praktyce myślę, że „gdzie mogę to zrobić z kodem, który ludzie zrozumieją” jest najważniejszym czynnikiem.
źródło
Aby uprościć odpowiedź na to pytanie, należy spojrzeć na równoważenie obciążenia. Chcesz umieścić ładunek tam, gdzie masz największą pojemność (jeśli ma to jakiś sens). W większości systemów to serwer SQL szybko staje się wąskim gardłem, więc prawdopodobnie odpowiedź jest taka, że nie chcesz, aby SQL wykonywał o jedną uncję pracy więcej niż musi.
Również w większości architektur to serwer (y) SQL stanowią rdzeń systemu, a systemy zewnętrzne są dodawane.
Ale powyższa matematyka jest tak trywialna, że jeśli nie pchasz swojego systemu do granic możliwości, najlepszym miejscem do umieszczenia jest tam, gdzie chcesz. Gdyby matematyka nie była trywialna, jak na przykład obliczanie sin / cos / tan dla, powiedzmy, obliczenia odległości, wysiłek może stać się nietrywialny i wymagać starannego planowania i testowania.
źródło
Inne odpowiedzi na to pytanie są interesujące. O dziwo, nikt nie odpowiedział na twoje pytanie. Zastanawiasz się:
Więcej informacji: W przypadku pierwszego pytania chcesz mieć pewność, że agregowanie ułamków działa bez błędów zaokrągleń. Myślę, że liczba 19,2 jest rozsądna dla pieniędzy, aw drugim przypadku liczby całkowite są OK. Z tego powodu używanie pływaka dla pieniędzy jest niewłaściwe.
Jeśli chodzi o pytanie drugie, jako programista lubię mieć pełną kontrolę nad datą uznawaną za „teraz”. Podczas korzystania z funkcji takich jak now () może być trudno napisać automatyczne testy jednostkowe. Ponadto, jeśli masz dłuższy skrypt transakcji, dobrze jest ustawić zmienną równą now () i użyć tej zmiennej, aby cała logika używała dokładnie tej samej wartości.
źródło
Podam prawdziwy przykład, aby odpowiedzieć na to pytanie
Musiałem obliczyć ważoną średnią kroczącą na moich danych ohlc, mam około 134000 świec z symbolem dla każdej, aby to zrobić
Który jest lepszy?
Wymagania
Aby dać ci trochę zachęty, jest to wersja Pythona, która wykonuje ważoną średnią ruchomą
WMA wykonane za pomocą kodu
WMA przez SQL
Wierz lub nie, ale zapytanie działa szybciej niż wersja Pure Python, wykonująca WAŻONĄ ŚREDNĄ RUCHOMĄ !!! Podszedłem krok po kroku do napisania tego zapytania, więc trzymaj się tego, a wszystko będzie dobrze
Prędkość
0,42141127300055814 sekund Python
0,23801879299935536 sekund SQL
Mam 134000 fałszywych rekordów OHLC w mojej bazie danych podzielonych na 1000 akcji, co jest przykładem sytuacji, w których SQL może przewyższać serwer aplikacji
źródło