Czy możliwe jest asynchroniczne wywołanie jdbc?

158

Zastanawiam się, czy istnieje sposób na wykonywanie asynchronicznych wywołań do bazy danych?

Na przykład wyobraź sobie, że mam duże żądanie, którego przetworzenie zajmuje bardzo dużo czasu, chcę wysłać żądanie i otrzymać powiadomienie, gdy żądanie zwróci wartość (przekazując Listener / callback lub coś w tym rodzaju). Nie chcę blokować czekania na odpowiedź bazy danych.

Nie uważam, że użycie puli wątków jest rozwiązaniem, ponieważ nie jest skalowalne, w przypadku ciężkich współbieżnych żądań spowoduje to powstanie bardzo dużej liczby wątków.

Mamy do czynienia z tego rodzaju problemem z serwerami sieciowymi i znaleźliśmy rozwiązania za pomocą wywołania systemowego select / poll / epoll, aby uniknąć posiadania jednego wątku na połączenie. Zastanawiam się tylko, jak mieć podobną funkcję z żądaniem bazy danych?

Uwaga: zdaję sobie sprawę, że użycie FixedThreadPool może być dobrym obejściem, ale jestem zaskoczony, że nikt nie opracował systemu naprawdę asynchronicznego (bez użycia dodatkowego wątku).

** Aktualizacja **
Ze względu na brak realnych praktycznych rozwiązań postanowiłem sam stworzyć bibliotekę (część finagle): finagle-mysql . Zasadniczo dekoduje / dekoduje żądanie / odpowiedź mysql i używa Finagle / Netty pod maską. Bardzo dobrze skaluje się nawet przy dużej liczbie połączeń.

Steve Gury
źródło
1
Zobacz także github.com/mauricio/postgresql-async
Daniel Worthington-Bodart
Problem polega na tym, w jaki sposób baza danych może powiadomić klienta o zakończeniu zapytania. Jednym z nich byłoby (np.) Oracle skorzystanie z funkcji „Powiadomienie o zmianie wyniku zapytania bazy danych” i otrzymywanie powiadomienia o zmianie danych w bazie danych. Dotyczy to zapytań SQL, które modyfikują dane bazy danych. W przypadku zapytań tylko do odczytu to nie zadziała. Z drugiej strony nie jestem pewien, czy tworzenie połączeń asynchronicznych byłoby dobrym pomysłem, ponieważ ich ustanowienie jest drogie. Oczywiście nie jest to zbyt ogólne rozwiązanie. Tylko do przemyślenia ...
Mike Argyriou
Czy finagle-mysql używa JDBC?
Saeed Zarinfam,

Odpowiedzi:

164

Nie rozumiem, w jaki sposób którekolwiek z proponowanych podejść, które zawijają wywołania JDBC w aktorach, wykonawcach lub cokolwiek innego, może tutaj pomóc - czy ktoś może to wyjaśnić.

Z pewnością podstawowym problemem jest blokowanie operacji JDBC na gnieździe IO. Kiedy to robi, blokuje uruchamianie wątku - koniec historii. Niezależnie od wybranego frameworka zawijania, jeden wątek będzie zajęty / zablokowany na współbieżne żądanie.

Jeśli podstawowe sterowniki bazy danych (MySql?) Oferują sposób przechwycenia tworzenia gniazda (patrz SocketFactory), wyobrażam sobie, że byłoby możliwe zbudowanie warstwy bazy danych sterowanej zdarzeniami asynchronicznymi na wierzchu interfejsu API JDBC, ale musielibyśmy hermetyzować całe JDBC za fasadą sterowaną wydarzeniami, a ta fasada nie wyglądałaby jak JDBC (po tym, jak byłaby sterowana zdarzeniami). Przetwarzanie bazy danych odbywałoby się asynchronicznie w innym wątku z wywołującym i musiałbyś wymyślić, jak zbudować menedżera transakcji, który nie opiera się na koligacji wątków.

Coś podobnego do podejścia, o którym wspomniałem, pozwoliłoby nawet pojedynczemu wątkowi w tle na przetworzenie obciążenia równoczesnych plików exec JDBC. W praktyce prawdopodobnie uruchomiłbyś pulę wątków, aby wykorzystać wiele rdzeni.

(Oczywiście nie komentuję logiki pierwotnego pytania tylko odpowiedzi, które sugerują, że współbieżność w scenariuszu z blokowaniem wejścia / wyjścia gniazda jest możliwa bez użytkownika wzorca selektora - prostsze jest po prostu obliczenie typowej współbieżności JDBC i umieszczenie w puli połączeń o odpowiedniej wielkości).


Wygląda na to, że MySql prawdopodobnie robi coś podobnego do sugerowanego przeze mnie --- http://code.google.com/p/async-mysql-connector/wiki/UsageExample

johnlon
źródło
1
Korzystanie z Akka nie powoduje wywołań do relacyjnych baz danych asynchronicznych. Pozwala na łatwe uruchamianie ich na kilku dedykowanych wątkach w celu uzyskania dostępu do bazy danych. W ten sposób nie wyłączasz całej witryny, gdy witryna przestaje odpowiadać, ponieważ zawsze wykonywałeś asynchroniczne wywołania w warstwie usługi do warstwy DAO z obietnicami, a wątki serwera WWW są oddzielone od reszty aplikacji.
Onur
Aktorzy nie są jedynymi obejściami (np. Mikrousługi i asynchroniczny http, które skalujemy do tysięcy na sekundę) i nie byłbym tak szybki, aby odrzucić ich jako nieasynchroniczne z punktu widzenia klienta. Jeśli 1k wątków UI trafia do twojego systemu i tylko 10 wątków jest blokowanych w bazie danych, podczas gdy 990 "wiadomości" (lub coś podobnego) jest umieszczanych w kolejce w pamięci bez blokowania żadnego z 1k wątków UI (które prawdopodobnie zostaną zwolnione). .. czy to nie jest wymagane? Bardzo chciałbym zobaczyć prawdziwy asynchroniczny JDBC, ale to nie znaczy, że w międzyczasie nie ma wyjątkowo wykonalnych obejść.
Greg Pendlebury
42

Nie można wykonać asynchronicznego wywołania do bazy danych przez JDBC, ale można wykonywać asynchroniczne wywołania do JDBC za pomocą Actors (np. Aktor wykonuje wywołania bazy danych przez JDBC i wysyła wiadomości do osób trzecich, gdy połączenia się kończą), lub, jeśli lubisz CPS, z potokowymi kontraktami futures (obietnicami) (dobrą implementacją jest Scalaz Promises )

Nie uważam, że użycie puli wątków jest rozwiązaniem, ponieważ nie jest skalowalne, w przypadku ciężkich współbieżnych żądań spowoduje to powstanie bardzo dużej liczby wątków.

Aktorzy Scala są domyślnie oparte na zdarzeniach (a nie na wątkach) - planowanie kontynuacji umożliwia tworzenie milionów aktorów w standardowej konfiguracji JVM.

Jeśli celujesz w Javę, Akka Framework to implementacja modelu Actor, która ma dobry interfejs API zarówno dla języka Java, jak i Scala.


Poza tym synchroniczna natura JDBC ma dla mnie sens. Koszt sesji bazy danych jest znacznie wyższy niż koszt zablokowania wątku Java (w tle lub w tle) i oczekiwania na odpowiedź. Jeśli Twoje zapytania działają tak długo, że możliwości usługi wykonawczej (lub zawijania struktur współbieżności Actor / fork-join / promise) nie są dla Ciebie wystarczające (i zużywasz zbyt wiele wątków), powinieneś przede wszystkim pomyśleć o ładowanie bazy danych. Zwykle odpowiedź z bazy danych wraca bardzo szybko, a usługa wykonawcza wspierana przez stałą pulę wątków jest wystarczająco dobrym rozwiązaniem. Jeśli masz zbyt wiele długo działających zapytań, powinieneś rozważyć wstępne (wstępne) przetwarzanie - na przykład nocne przeliczanie danych lub coś w tym rodzaju.

Vasil Remeniuk
źródło
2
@Victor, każdy aktor pracujący równolegle nad operacją blokowania (JDBC) będzie działał na osobnym wątku, którego Steve stara się uniknąć
Vasil Remeniuk
36
Podejście aktora nadal wymaga jednego wątku na aktywną transakcję bazy danych, podczas gdy transakcja jest w toku, więc nie jest to tak naprawdę rozwiązanie problemu OP, chyba że chcesz ograniczyć liczbę równoległych transakcji w bazie danych i poczekać kilka operacji "asynchronicznych" na bazie danych dla niektórych już wykonujących, aby zakończyć i zwolnić wątek. Nie jest to jednak zły pomysł - baza danych może zostać przeciążona, jeśli otworzysz zbyt wiele połączeń - więc pomoże Ci umieszczenie transakcji bazy danych w kolejce do przetwarzania zamiast blokowania wątku przetwarzania żądania HTTP.
Dobes Vandermeer
8
Rozwiązanie oparte na aktorach nadal blokuje wątek. Nie mów, że nie można wykonać asynchronicznego wywołania jdbc, istnieją eksperymentalne biblioteki open source, które próbują zaimplementować asynchroniczne wywołanie jdbc.
6
+1 „Koszt sesji bazy danych jest znacznie wyższy niż koszt zablokowania wątku Java”
Paul Draper,
1
W przypadku drogich połączeń DB zwykle nie ma takiego dużego problemu. Dopiero gdy połączenie jest trywialne, narzut sieci staje się problemem. Jeśli chcesz wykonać 100 zapytań, które trwają 1 ms na DB, ale obciążenie sieci wynosi 200 ms, to zajmie to ponad 20 sekund synchronicznie, ale 300 ms asynchronicznie.
morten
12

Być może przydałby się asynchroniczny system przesyłania wiadomości JMS, który całkiem dobrze się skaluje, IMHO:

  • Wyślij wiadomość do kolejki, gdzie subskrybenci zaakceptują wiadomość i uruchomią proces SQL. Twój główny proces będzie nadal działać i będzie akceptować lub wysyłać nowe żądania.

  • Po zakończeniu procesu SQL możesz uruchomić go w odwrotny sposób: wysłać wiadomość do ResponseQueue z wynikiem procesu, a odbiornik po stronie klienta zaakceptuje go i uruchom kod wywołania zwrotnego.

Tomas Narros
źródło
7

Nie ma bezpośredniego wsparcia w JDBC, ale masz wiele opcji, takich jak MDB, Executory z Java 5.

„Nie uważam, że użycie puli wątków jest rozwiązaniem, ponieważ nie jest skalowalne. W przypadku wielu jednoczesnych żądań spowoduje to powstanie bardzo dużej liczby wątków”.

Ciekawi mnie, dlaczego ograniczona pula wątków nie miałaby się skalować? Jest to pula, a nie wątek na żądanie, służąca do tworzenia wątku na każde żądanie. Używam tego od jakiegoś czasu na mocno obciążonej aplikacji internetowej i do tej pory nie widzieliśmy żadnych problemów.

Aravind Yarram
źródło
Myślę, że głównym argumentem przeciwko wątkom jest to, że zasadniczo znajdujesz się poza wszelkimi standardowymi ograniczeniami kontenerów Java, więc tracisz klastrowanie zarządzane przez kontener i przełączasz możliwości awaryjne, chociaż możesz toczyć własne lub użyć czegoś takiego jak Terracotta.
mezmo
3
możemy korzystać z ankiet wątków zarządzanych przez serwer aplikacji za pomocą menedżerów pracy. websphere, weblogic i glassfish wspierają to
Aravind Yarram
4

Jak wspomniano w innych odpowiedziach, JDBC API nie jest z natury asynchroniczne.
Jeśli jednak możesz żyć z podzbiorem operacji i innym interfejsem API, istnieją rozwiązania. Jednym z przykładów jest https://github.com/jasync-sql/jasync-sql, który działa dla MySQL i PostgreSQL.

oshai
źródło
3

Projekt Ajdbc wydaje się odpowiadać na ten problem http://code.google.com/p/adbcj/

Obecnie istnieją 2 eksperymentalne sterowniki natywnie asynchroniczne dla mysql i postgresql.

Sebastien
źródło
Chciałbym mieć gotowe takie podejście. JDBC od samego początku bardzo ewoluował (iteratory, szablony, przygotowane procedury), ale to podejście asynchroniczne nigdy nie zostało zaimplementowane. Byłoby to szczególnie interesujące w przypadku operacji zapisu (wstawianie, aktualizowanie, usuwanie), a szczególnie tych ciężkich partii TX, z którymi wszyscy mamy do czynienia. Moim zdaniem każde podejście oparte na kliencie (łączenie, aktor, planowanie, przesyłanie wiadomości ...) przyniosłoby niewielkie korzyści w zakresie wykorzystania zasobów (prawdopodobnie pewne zyski w przepustowości lub opóźnieniu).
Jaime Casero
Stare i porzucone, obsługiwane tylko dwa typy danych i nie są nawet gotowe do produkcji. Niestety :(
Aaron Zinman
Wydanie 1 tej biblioteki dotyczy niedostępności witryny internetowej . Ma więcej niż rok. Podejrzewam, że ta biblioteka jest całkiem martwa.
Lukas Eder
3

Stare pytanie, ale trochę więcej informacji. Nie jest możliwe, aby JDBC wysyłał asynchroniczne żądania do samej bazy danych, chyba że dostawca zapewni rozszerzenie JDBC i opakowanie do obsługi JDBC. To powiedziawszy, możliwe jest owinięcie samego JDBC kolejką przetwarzania i zaimplementowanie logiki, która może przetwarzać poza kolejką w jednym lub większej liczbie oddzielnych połączeń. Jedną z zalet tego w przypadku niektórych typów wywołań jest to, że logika, jeśli jest wystarczająco obciążona, może konwertować wywołania na partie JDBC do przetwarzania, co może znacznie przyspieszyć logikę. Jest to najbardziej przydatne w przypadku połączeń, w których wstawiane są dane, a rzeczywisty wynik musi zostać zarejestrowany tylko w przypadku wystąpienia błędu. Doskonałym tego przykładem jest sytuacja, w której operacje wstawiania są wykonywane w celu rejestrowania aktywności użytkownika. Aplikacja wygrała ”

Na marginesie, jeden produkt na rynku zapewnia podejście oparte na polityce, pozwalające na asynchroniczne wykonywanie wywołań asynchronicznych, takich jak te, które opisałem ( http://www.heimdalldata.com/ ). Zastrzeżenie: jestem współzałożycielem tej firmy. Umożliwia stosowanie wyrażeń regularnych do żądań transformacji danych, takich jak wstawianie / aktualizowanie / usuwanie dowolnego źródła danych JDBC, i automatycznie łączy je w partie w celu przetworzenia. W połączeniu z MySQL i opcją rewriteBatchedStatements ( MySQL i JDBC z rewriteBatchedStatements = true ) może to znacznie zmniejszyć ogólne obciążenie bazy danych.

Erik Brandsberg
źródło
Ale to nadal oznacza, że ​​JDBC powinien mieć co najmniej jeden oddzielny wątek. A co z frameworkami i stosami, które są jednowątkowe, ale nadal oparte na wywołaniach zwrotnych (przychodzi na myśl nodejs)? Czy wiesz, jak zarządzają połączeniami JDBC?
yuranos
3

Moim zdaniem masz trzy opcje:

  1. Użyj kolejki współbieżnej, aby dystrybuować wiadomości w małej i stałej liczbie wątków. Więc jeśli masz 1000 połączeń, będziesz mieć 4 wątki, a nie 1000 wątków.
  2. Uzyskaj dostęp do bazy danych w innym węźle (tj. Innym procesie lub maszynie) i poproś klienta bazy danych o wykonanie asynchronicznych wywołań sieciowych do tego węzła.
  3. Zaimplementuj prawdziwy system rozproszony za pomocą komunikatów asynchronicznych. W tym celu będziesz potrzebować kolejki wiadomości, takiej jak CoralMQ lub Tibco.

Diclaimer: Jestem jednym z twórców CoralMQ.

rdalmeida
źródło
3

Opracowywane jest rozwiązanie umożliwiające reaktywną łączność ze standardowymi relacyjnymi bazami danych.

Osoby, które chcą skalować, zachowując wykorzystanie relacyjnych baz danych, są odcięte od programowania reaktywnego ze względu na istniejące standardy oparte na blokowaniu operacji we / wy. R2DBC określa nowy interfejs API, który umożliwia reaktywny kod, który efektywnie współpracuje z relacyjnymi bazami danych.

R2DBC to specyfikacja zaprojektowana od podstaw do programowania reaktywnego z bazami danych SQL definiująca nieblokujący interfejs SPI dla implementatorów sterowników baz danych i autorów bibliotek klienckich. Sterowniki R2DBC w pełni implementują protokół przewodowy bazy danych na szczycie nieblokującej warstwy we / wy.

Witryna internetowa R2DBC

GitHub R2DBC

Matryca funkcji

wprowadź opis obrazu tutaj

Yassin Hajaj
źródło
2

W Java 5,0 wykonawcy może się przydać.

Możesz mieć stałą liczbę wątków do obsługi długotrwałych operacji. I zamiast Runnableciebie możesz użyć Callable, które zwracają wynik. Wynik jest zamknięty w Future<ReturnType>obiekcie, dzięki czemu można go uzyskać, gdy wróci.

Bozho
źródło
2

Po prostu szalony pomysł: możesz użyć wzorca Iteratee zamiast zestawu wyników JBDC zawiniętego w trochę Future / Promise

Hammersmith robi to dla MongoDB .

jwinandy
źródło
1

Myślę tutaj o pomysłach. Dlaczego nie można mieć puli połączeń z bazą danych, z których każde ma wątek. Każdy wątek ma dostęp do kolejki. Jeśli chcesz wykonać zapytanie, które zajmuje dużo czasu, możesz umieścić w kolejce, a wtedy jeden z wątków podejmie je i obsłuży. Nigdy nie będziesz mieć zbyt wielu wątków, ponieważ liczba Twoich wątków jest ograniczona.

Edycja: Lub jeszcze lepiej, tylko kilka wątków. Gdy wątek widzi coś w kolejce, prosi o połączenie z puli i obsługuje je.

Amir Raminfar
źródło
1

Biblioteka commons-dbutils obsługuje plik, AsyncQueryRunnerktóry podajesz ExecutorServicedo, i zwraca plik Future. Warto to sprawdzić, ponieważ jest łatwy w użyciu i zapewnia, że ​​nie wycieknie zasoby.

William Speirs
źródło
1

Jeśli jesteś zainteresowany asynchronicznymi interfejsami API baz danych dla języka Java, powinieneś wiedzieć, że pojawiła się nowa inicjatywa polegająca na opracowaniu zestawu standardowych interfejsów API opartych na CompletableFuture i lambdach. Istnieje również implementacja tych interfejsów API przez JDBC, której można użyć do ćwiczenia tych interfejsów API: https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ JavaDoc jest wymieniona w pliku README projekt github.

Jean de Lavarene
źródło