非同期のjdbc呼び出しは可能ですか?


158

データベースへの非同期呼び出しを行う方法はあるのでしょうか。

たとえば、処理に非常に長い時間がかかる大きなリクエストがあるとします。リクエストを送信し、リクエストが値を返すときに(リスナー/コールバックなどを渡すことによって)通知を受け取りたいと考えています。データベースが応答するのを待つのをブロックしたくありません。

スレッドのプールを使用することは、スケーリングしないので解決策であるとは思いません。大量の同時リクエストの場合、これは非常に多数のスレッドを生成します。

この種のネットワークサーバーの問題に直面しており、select / poll / epollシステムコールを使用して、接続ごとに1つのスレッドが存在しないようにすることで解決策を見つけました。データベースリクエストで同様の機能を使用する方法を知りたいだけですか?

注:FixedThreadPoolを使用するのが適切な回避策である可能性があることは承知していますが、(余分なスレッドを使用せずに)本当に非同期のシステムを開発した人がいないことに驚いています。

**更新**
実際の実用的なソリューションがないため、私は自分でライブラリ(finagleの一部)を作成することにしました:finagle-mysql。それは基本的にmysql要求/応答をデコード/デコードし、内部でFinagle / Nettyを使用します。接続数が非常に多い場合でも、非常によく拡張されます。




問題は、クエリが終了したときにdbがクライアントに通知する方法です。1つは、(たとえば)Oracleが「データベースクエリ結果変更通知」機能を使用して、dbデータが変更されたときに通知を受けることです。これは、dbデータを変更するSQLクエリに適用されます。読み取り専用クエリの場合、これは機能しません。一方、接続を確立することはコストがかかるため、接続を非同期にすることは良いアイデアになるかどうかはわかりません。もちろん、これは非常に一般的な解決策ではありません。ただ考えるための食べ物...
マイク・アルギリウ

finagle-mysqlはJDBCを使用しますか?
Saeed Zarinfam 2017年

回答:


164

アクター、エグゼキュータなどでJDBC呼び出しをラップする提案されたアプローチがどのようにここで役立つのか理解できません-誰かが明確にすることができますか?

確かに基本的な問題は、JDBC操作がソケットIOでブロックすることです。これを行うと、スレッドの実行がブロックされます-話の終わり。使用するラッピングフレームワークがどのようなものであっても、同時要求ごとに1つのスレッドがビジー状態またはブロックされたままになります。

基になるデータベースドライバー(MySql?)がソケットの作成をインターセプトする手段を提供する場合(SocketFactoryを参照)、JDBC APIの上に非同期イベント駆動型データベースレイヤーを構築することは可能ですが、カプセル化する必要があります。イベントドリブンファサードの背後にあるJDBC全体。そのファサードは、JDBCのようには見えません(イベントドリブンになると)。データベースの処理は、呼び出し元とは別のスレッドで非同期に行われるため、スレッドアフィニティに依存しないトランザクションマネージャを構築する方法を考え出す必要があります。

私が言及するアプローチのようなものは、単一のバックグラウンドスレッドでさえ、同時JDBC execの負荷を処理することを可能にします。実際には、複数のコアを利用するためにスレッドのプールを実行するでしょう。

(もちろん、元の質問のロジックについてはコメントしていません。セレクターパターンを使用しなくても、ソケットIOをブロックするシナリオでの同時実行が可能であることを示す応答だけです。適切なサイズの接続プールで)。


MySqlはおそらく私が提案している行に沿って何かを行うようです--- http://code.google.com/p/async-mysql-connector/wiki/UsageExample


1
Akkaを使用しても、リレーショナルDBへの呼び出しは非同期ではありません。これにより、DBアクセス用の専用スレッドの束でそれらを簡単に実行できます。この方法では、サイトが応答しなくなったときにサイト全体を停止することはありません。これは、promiseを使用してサービスレイヤーで常にDAOレイヤーに対して非同期呼び出しを行っており、Webサーバースレッドがアプリケーションの残りの部分から分離されているためです。
Onur

回避策はアクターだけではありません(たとえば、マイクロサービスと非同期http。毎秒数千にスケーリングします)。クライアントの観点からすると、非同期ではないので、すぐに却下します。1K UIスレッドトラフィックは、あなたのシステムを入力して、唯一の10のスレッドがDB上でブロックされている場合は、990「メッセージ」(または類似した何か)しながらメモリにqueue'dされずに遮断する任意の(おそらく発売予定)1kのUIスレッドのを。 ..必要なことではないですか?私は真の非同期JDBCを見たいと思っていますが、それは当面は非常に実行可能な回避策がないという意味ではありません。
Greg Pendlebury、2015年

42

これは、非同期呼び出しすることは不可能だ、データベースへの JDBC経由しますが、非同期呼び出しすることができますJDBCにし俳優、(例えば、俳優がJDBC経由でDBへの呼び出しを行い、通話が終わったとき、第三者にメッセージを送信します)または、CPSが好きな場合は、パイプラインフューチャー(プロミス)を使用します(適切な実装はScalaz プロミスです

スレッドのプールを使用することは、スケーリングしないので解決策であるとは思いません。大量の同時リクエストの場合、これは非常に多数のスレッドを生成します。

デフォルトでは、Scalaアクターはイベントベース(スレッドベースではない)です。継続スケジューリングにより、標準のJVMセットアップで数百万のアクターを作成できます。

Javaをターゲットにしている場合、Akka Frameworkは、JavaとScalaの両方に優れたAPIを持つアクターモデルの実装です。


それとは別に、JDBCの同期の性質は私には完全に理にかなっています。データベースセッションのコストは、Javaスレッドが(フォアグラウンドまたはバックグラウンドで)ブロックされ、応答を待機するコストよりもはるかに高くなります。クエリの実行時間が長すぎて、executorサービス(またはActor / fork-join / promise同時実行フレームワークをラップする)の機能では不十分な場合(そして、スレッドを大量に消費している場合)、まず最初に、データベースの負荷。通常、データベースからの応答は非常に速く返されるため、固定スレッドプールでバックアップされたエグゼキューターサービスは十分なソリューションです。実行時間の長いクエリが多すぎる場合は、事前の(事前)処理を検討する必要があります。たとえば、毎晩のデータの再計算などです。


2
@Victor、ブロッキング操作(JDBC)で並行して動作するすべてのアクターは、スティーブが回避しようとしている別のスレッドで実行されます
Vasil Remeniuk

36
アクターアプローチでは、トランザクションが進行している間も、アクティブなデータベーストランザクションごとに1つのスレッドが必要であるため、並列データベーストランザクションの数を制限し、いくつかの「非同期」データベース操作を待機させない限り、これはOPの問題の実際の解決策ではありません。一部のすでに実行中のスレッドを終了してスレッドを解放するため。ただし、これは悪い考えではありません。開いている接続が多すぎると、データベースが過負荷になる可能性があるため、httpリクエスト処理スレッドをブロックする代わりに、データベーストランザクションをキューに入れて処理することが役立ちます。
Dobes Vandermeer 2012年

8
アクターベースのソリューションはまだスレッドをブロックしています。非同期jdbc呼び出しを実行することは不可能であると言わないでください。非同期jdbcを実装しようとする実験的なオープンソースライブラリがあります。

6
+1「データベースセッションのコストは、ブロックされているJavaスレッドのコストよりもはるかに高い」
Paul Draper

1
高価なDB呼び出しの場合、通常、それほど大きな問題はありません。ネットワークのオーバーヘッドが問題になるのは、呼び出しが簡単な場合です。各DBで1ミリ秒かかる100のクエリを実行したいが、ネットワークのオーバーヘッドが200ミリ秒である場合、同期的に20秒以上かかりますが、非同期的には300ミリ秒かかります。
モルテン

12

おそらく、拡張性の高いJMS非同期メッセージングシステムを使用できます。

  • サブスクライバーがメッセージを受け入れるキューにメッセージを送信し、SQLプロセスを実行します。メインプロセスは引き続き実行され、新しいリクエストを受け入れまたは送信します。

  • SQLプロセスが終了したら、逆の方法で実行できます。つまり、プロセスの結果を含むメッセージをResponseQueueに送信し、クライアント側のリスナーがそれを受け入れてコールバックコードを実行します。


7

JDBCには直接のサポートはありませんが、MDB、Java 5のエグゼキューターなど、複数のオプションがあります。

「スレッドのプールを使用することは、スケーリングされないため、解決策であるとは考えていません。大量の同時リクエストの場合、これにより、非常に多数のスレッドが生成されます。」

スレッドの制限付きプールがスケーリングされないのはなぜですか?リクエストごとにスレッドを生成するのは、リクエストごとのスレッドではなくプールです。高負荷のウェブアプリでかなり長い間これを使用してきましたが、今のところ問題は見られません。


スレッドに対する主な議論は、基本的には標準のJavaコンテナー制約の外にいるため、コンテナーを管理するクラスター化を失い、機能をフェイルオーバーすることですが、独自のものをロールしたり、Terracottaのようなものを使用したりできます。
mezmo

3
ワークマネージャーを使用して、アプリサーバーが管理するスレッドポーリングを利用できます。websphere、weblogic、
glassfishが


4

他の回答で述べたように、JDBC APIはその性質上非同期ではありません。
ただし、操作のサブセットと異なるAPIを使用できる場合は、解決策があります。1つの例は、MySQLおよびPostgreSQLで機能するhttps://github.com/jasync-sql/jasync-sqlです。


3

Ajdbcプロジェクトはこの問題を解決するようですhttp://code.google.com/p/adbcj/

現在、mysqlおよびpostgresql用の2つの実験的なネイティブ非同期ドライバーがあります。


このアプローチを準備したいと思います。JDBCは最初からかなり進化してきましたが(イテレータ、テンプレート、準備されたプロシージャ)、この非同期アプローチは実装されていません。これは、書き込み操作(挿入、更新、削除)、特に私たち全員が直面する大量のバッチTXにとって特に興味深いでしょう。私の意見では、あらゆる種類のクライアントベースのアプローチ(プーリング、アクター、スケジューリング、メッセージング...)は、リソースの使用に関してはほとんど報酬をもたらさないでしょう(おそらくスループットまたはレイテンシのいくつかの向上)。
Jaime Casero、2015

古く、放棄された、2つのデータ型のみがサポートされており、本番環境に近いものではありません。残念ながら:(
Aaron Zinman 2015年

このライブラリの第1号は、利用できないWebサイトに関するものです。1年以上経過しています。このライブラリはかなり死んでいると思います。
Lukas Eder

3

古い質問ですが、もう少し情報があります。ベンダーがJDBCの拡張機能とJDBCを処理するためのラッパーを提供しない限り、JDBCがデータベース自体に非同期要求を発行することはできません。つまり、JDBC自体を処理キューでラップし、1つ以上の個別の接続でキューから処理できるロジックを実装することが可能です。一部のタイプの呼び出しに対するこれの1つの利点は、十分な負荷がかかっている場合、ロジックが呼び出しを処理用のJDBCバッチに変換できるため、ロジックを大幅に高速化できることです。これは、データが挿入されている呼び出しに最も役立ちます。実際の結果は、エラーが発生した場合にのみ記録する必要があります。この良い例は、ユーザーのアクティビティをログに記録するために挿入が実行されている場合です。アプリケーションは

余談ですが、市場に出回っている製品の1つは、私が説明したような非同期呼び出しを非同期で実行できるようにするポリシー主導のアプローチを提供しています(http://www.heimdalldata.com/)。免責事項:私はこの会社の共同創設者です。これにより、任意のJDBCデータソースの挿入/更新/削除などのデータ変換要求に正規表現を適用でき、それらを自動的にバッチ処理して処理します。MySQLとrewriteBatchedStatementsオプション(rewriteBatchedStatements = trueを指定したMySQLおよびJDBC)で使用すると、データベース全体の負荷を大幅に削減できます。


ただし、これは、JDBCには少なくとも1つの個別のスレッドが必要であることを意味します。シングルスレッドでありながらコールバックベースのフレームワークとスタックはどうですか(nodejsが思い浮かびます)?彼らがJDBC呼び出しをどのように管理するか知っていますか?
ユラノス2017年

3

私の意見では、3つのオプションがあります。

  1. 並行キューを使用して、少数の固定数のスレッドにメッセージを分散します。したがって、1000個の接続がある場合、1000スレッドではなく4スレッドになります。
  2. 別のノード(つまり、別のプロセスまたはマシン)でデータベースアクセスを行い、データベースクライアントにそのノードへの非同期ネットワーク呼び出しを行わせます。
  3. 非同期メッセージを通じて真の分散システムを実装します。そのためには、CoralMQやTibcoなどのメッセージングキューが必要です。

Diclaimer:私はCoralMQの開発者の1人です。


3

標準のリレーショナルデータベースを使用してリアクティブ接続を可能にするソリューションが開発されています。

リレーショナルデータベースの使用を維持しながら拡張したい人々は、ブロックI / Oに基づく既存の標準により、リアクティブプログラミングから切り離されます。R2DBCは、リレーショナルデータベースで効率的に機能するリアクティブコードを可能にする新しいAPIを指定しています。

R2DBCは、データベースドライバーのインプリメンターおよびクライアントライブラリの作成者向けにノンブロッキングSPIを定義するSQLデータベースを使用したリアクティブプログラミングのためにゼロから設計された仕様です。R2DBCドライバーは、ノンブロッキングI / Oレイヤーの上にデータベースワイヤープロトコルを完全に実装します。

R2DBCのWebサイト

R2DBCのGitHub

機能マトリックス

ここに画像の説明を入力してください


2

Javaの5.0執行は便利来るかもしれません。

実行時間の長い操作を処理するために、固定数のスレッドを使用できます。そして、代わりに結果を返すをRunnable使用できますCallable。結果はFuture<ReturnType>オブジェクトにカプセル化されるため、戻ったときに取得できます。



2

ただおかしなアイデア:いくつかのFuture / PromiseでラップされたJBDC resultSetでIterateeパターンを使用できます

ハマースミスはMongoDBのためにそれを行います。


1

ここでアイデアを考えています。それぞれにスレッドがあるデータベース接続のプールを作成できなかったのはなぜですか。各スレッドはキューにアクセスできます。時間がかかるクエリを実行する場合は、キューに入れると、スレッドの1つがそれを取得して処理します。スレッドの数は制限されているため、スレッドが多すぎることはありません。

編集:またはもっと良いことに、いくつかのスレッドだけです。スレッドはキュー内の何かを見つけると、プールからの接続を要求してそれを処理します。


1

commons-dbutilsライブラリはto AsyncQueryRunnerを提供するExecutorServiceをサポートしており、を返しますFuture。使い方は簡単で、リソースがリークしないことを確認するためにチェックアウトする価値があります。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.