データベースへの非同期呼び出しを行う方法があるのだろうか?
たとえば、処理に非常に長い時間がかかる大きなリクエストがあるとします。リクエストが値を返すとき(リスナー/コールバックなどを渡すことで)リクエストを送信し、通知を受け取りたいと思います。データベースが応答するのをブロックしたくありません。
スレッドのプールを使用することは、スケーリングしないため、ソリューションとは考えません。重い同時リクエストの場合、これは非常に多くのスレッドを生成します。
ネットワークサーバーに関するこの種の問題に直面しており、select/poll/epollシステムコールを使用して、接続ごとに1つのスレッドを持たないようにすることで解決策を見つけました。データベースリクエストで同様の機能を使用するにはどうすればよいのでしょうか。
注:FixedThreadPoolを使用するのが良い回避策かもしれないことは承知していますが、(余分なスレッドを使用せずに)本当に非同期なシステムを開発した人がいないことに驚いています。
**更新**
実際の実用的な解決策がないため、私は自分でライブラリ(finagleの一部)を作成することにしました: finagle-mysql 。基本的にmysqlリクエスト/レスポンスをデコード/デコードし、内部でFinagle/Nettyを使用します。膨大な数の接続がある場合でも、非常にうまくスケーリングします。
アクター、エグゼキューター、その他でJDBC呼び出しをラップする提案されたアプローチがここでどのように役立つか理解できません-誰かが明確にすることができます。
確かに基本的な問題は、JDBC操作がソケットIOでブロックすることです。これを実行すると、実行中のスレッドがブロックされます-ストーリーの終わり。どのラッピングフレームワークを使用する場合でも、同時要求ごとに1つのスレッドがビジー/ブロックされたままになることになります。
基礎となるデータベースドライバー(MySql?)がソケットの作成をインターセプトする手段を提供する場合(SocketFactoryを参照)、JDBC APIの上に非同期イベント駆動型データベースレイヤーを構築することが可能になると思いますが、イベント駆動型のファサードの背後にあるJDBC全体、およびそのファサードはJDBCのようには見えません(イベント駆動型になった後)。データベースの処理は呼び出し元とは異なるスレッドで非同期に発生するため、スレッドアフィニティに依存しないトランザクションマネージャの構築方法を検討する必要があります。
私が言及したアプローチのようなものは、単一のバックグラウンドスレッドでさえ、並行JDBC execの負荷を処理できるようにします。実際には、複数のコアを利用するために、おそらくスレッドのプールを実行します。
(もちろん、セレクタパターンのユーザーなしでソケットIOをブロックするシナリオでの同時実行が可能であることを示唆する応答だけを元の質問のロジックにコメントしているわけではありません。典型的なJDBCの同時実行性と適切なサイズの接続プールへの配置)。
MySqlはおそらく私が提案している行に沿って何かをするように見えます--- http://code.google.com/p/async-mysql-connector/wiki/UsageExample
非同期呼び出しをJDBC経由でデータベースに行うことはできませんが、非同期呼び出しをJDBCにActorsを使用して実行できます(たとえば、アクターはJDBC経由でDBを呼び出し、コールが終了したときにサードパーティにメッセージを送信します。または、CPSが好きな場合は、 パイプライン化された先物(約束) (適切な実装は Scalaz約束 )
スレッドのプールを使用することは、スケーリングしないため、ソリューションとは考えません。重い同時リクエストの場合、これは非常に多くのスレッドを生成します。
Scalaアクターはデフォルトでイベントベース(スレッドベースではない)です-継続スケジューリングにより、標準のJVM設定で何百万ものアクターを作成できます。
Javaをターゲットにしている場合、 Akka Framework は、JavaとScalaの両方に優れたAPIを備えたアクターモデルの実装です。
それとは別に、JDBCの同期的な性質は私にとって完全に理にかなっています。データベースセッションのコストは、(フォアまたはバックグラウンドで)ブロックされて応答を待機しているJavaスレッドのコストよりもはるかに高くなります。クエリが非常に長く実行されるため、executorサービス(またはActor/fork-join/promise同時実行フレームワークのラッピング)の機能では不十分な場合(およびスレッドを多く消費している場合)、まず、データベースの負荷。通常、データベースからの応答は非常に高速で返され、固定スレッドプールでバックアップされたexecutorサービスは十分なソリューションです。長時間実行されるクエリが多すぎる場合は、データの毎晩の再計算などのような事前(前)処理を検討する必要があります。
多分、あなたはJMS非同期メッセージングシステムを使うことができます。
サブスクライバーがメッセージを受け入れ、SQLプロセスを実行するキューにメッセージを送信します。メインプロセスは引き続き実行され、新しいリクエストを受け入れたり送信したりします。
SQLプロセスが終了したら、反対の方法で実行できます。プロセスの結果と共にメッセージをResponseQueueに送信し、クライアント側のリスナーがそれを受け入れてコールバックコードを実行します。
JDBCには直接のサポートはありませんが、MDB、Java 5のエグゼキューターなどの複数のオプションがあります。
「スレッドのプールを使用することは、スケーリングしないため、ソリューションとは思わない。同時リクエストが多い場合、これは非常に多くのスレッドを生成する。」
スレッドの境界プールが拡張されないのはなぜですか?要求ごとにスレッドを生成するのは、要求ごとのスレッドではないプールです。私はかなり長い間これを高負荷webappで使用してきましたが、これまでのところ何の問題も見ていません。
他の回答で述べたように、JDBC APIはその性質上非同期ではありません。
ただし、操作のサブセットと異なるAPIを使用できる場合は、解決策があります。 1つの例は https://github.com/jasync-sql/jasync-sql です。これはMySQLおよびPostgreSQLで機能します。
標準的なリレーショナルデータベースとのリアクティブな接続を可能にするソリューションが開発されています。
リレーショナルデータベースの使用を維持しながら拡張したい人は、I/Oのブロックに基づく既存の標準のために、リアクティブプログラミングから切り離されます。 R2DBCは、リレーショナルデータベースで効率的に動作するリアクティブコードを可能にする新しいAPIを指定します。
R2DBCは、データベースドライバの実装者とクライアントライブラリの作成者向けにノンブロッキングSPIを定義するSQLデータベースを使用したリアクティブプログラミング向けにゼロから設計された仕様です。 R2DBCドライバーは、ノンブロッキングI/Oレイヤーの上にデータベースワイヤプロトコルを完全に実装します。
古い質問ですが、さらに情報があります。ベンダーがJDBCの拡張機能とJDBCを処理するラッパーを提供しない限り、JDBCがデータベース自体に非同期要求を発行することはできません。ただし、JDBC自体を処理キューでラップし、1つ以上の個別の接続でキューを処理できるロジックを実装することができます。いくつかのタイプの呼び出しに対するこの利点の1つは、ロジックが十分な負荷がかかっている場合、処理のために呼び出しをJDBCバッチに変換できるため、ロジックを大幅に高速化できることです。これは、データが挿入される呼び出しに最も役立ち、エラーが発生した場合にのみ実際の結果を記録する必要があります。これの良い例は、ユーザーアクティビティを記録するために挿入が実行されている場合です。アプリケーションは、呼び出しがすぐに完了するか、数秒後に完了するかを気にしません。
補足として、市場に出回っている製品の1つでは、説明したような非同期呼び出しを非同期で行えるようにするポリシー駆動型のアプローチを提供しています( http://www.heimdalldata.com/ )。免責事項:私はこの会社の共同設立者です。 JDBCデータソースの挿入/更新/削除などのデータ変換リクエストに正規表現を適用でき、それらを処理のために自動的にバッチ処理します。 MySQLおよびrewriteBatchedStatementsオプション( rewriteBatchedStatements = trueを指定したMySQLおよびJDBC )と共に使用すると、データベースの全体的な負荷を大幅に削減できます。
私の意見では3つの選択肢があります:
免責事項:私はCoralMQの開発者の一人です。
Ajdbcプロジェクトはこの問題に答えているようです http://code.google.com/p/adbcj/
現在、mysqlおよびpostgresql用の2つの実験的なネイティブ非同期ドライバーがあります。
JavaOneで提示されたOracleからのノンブロッキングjdbc apiがどのように見えるかについての概要を次に示します。 CONF1578%2020160916.pdf
結局、本当に非同期のJDBC呼び出しが実際に可能になるようです。
Java 5.0 executors が便利かもしれません。
実行時間の長い操作を処理するために、固定数のスレッドを使用できます。また、Runnable
の代わりに、Callable
を使用して結果を返すことができます。結果は Future<ReturnType>
オブジェクトにカプセル化されるため、戻ったときに取得できます。
ただのクレイジーなアイデア:Future/PromiseにラップされたJBDC resultSetでIterateeパターンを使用できます
HammersmithはMongoDBに対してこれを行います。
Commons-dbutilsライブラリは、AsyncQueryRunner
を提供し、ExecutorService
を提供し、Future
を返します。使いやすく、リソースをリークしないようにチェックアウトする価値があります。
Javaの非同期データベースAPIに興味がある場合は、CompletableFutureとラムダに基づいた一連の標準APIを考案するための新しいイニシアチブがあることを知っておく必要があります。これらのAPIを実践するために使用できるJDBCを介したこれらのAPIの実装もあります。 https://github.com/Oracle/oracle-db-examples/tree/master/Java/AoJ JavaDocは、githubプロジェクトのREADMEに記載されています。
ここでアイデアを考えています。それぞれがスレッドを持つデータベース接続のプールを持てなかったのはなぜですか。各スレッドはキューにアクセスできます。長時間かかるクエリを実行したい場合、キューに入れると、スレッドの1つがそれを取得して処理します。スレッドの数が制限されているため、スレッドが多すぎることはありません。
編集:さらに良いことに、スレッドの数だけ。スレッドは、キュー内の何かを見つけると、プールからの接続を要求して処理します。
いくつかの簡単な解決策は、jdbs呼び出しをCompletableFuture
にラップし、これらの呼び出しにカスタムスレッドプールを提供することです。