Возможен ли асинхронный вызов jdbc?
интересно, есть ли способ сделать асинхронные вызовы в базу данных?
например, представьте, что у меня есть большой запрос, который занимает очень много времени для обработки, я хочу отправить запрос и получить уведомление, когда запрос вернет значение (передавая прослушиватель/обратный вызов или что-то еще). Я не хочу блокировать ожидание ответа базы данных.
Я не считаю, что использование пула потоков является решением, потому что оно не масштабируется, в случае тяжелые параллельные запросы это породит очень большое количество потоков.
мы столкнулись с такой проблемой с сетевыми серверами, и мы нашли решения с помощью системного вызова select/poll/epoll, чтобы избежать наличия одного потока на соединение. Мне просто интересно, как иметь аналогичную функцию с запросом базы данных?
Примечание.: Я знаю, что использование FixedThreadPool может быть хорошей работой, но я удивлен, что никто не разработал систему действительно асинхронную (без использования дополнительного потока).
** обновление **
Из-за отсутствия реальных практических решений, я решил создать библиотеку (часть ренонс) себя: finagle-mysql. Он в основном декодирует/декодирует запрос / ответ mysql и использует Finagle/Netty под капотом. Он очень хорошо масштабируется даже при огромном количестве соединений.
16 ответов:
Я не понимаю, как любой из предложенных подходов, которые обертывают вызовы JDBC в актерах, исполнителях или чем - то еще, могут помочь здесь-может кто-то уточнить.
конечно, основная проблема заключается в том, что блок операций JDBC на сокете IO. Когда он это делает, он блокирует поток, который работает в конце истории. Независимо от того, какую структуру обертывания вы решите использовать, в конечном итоге один поток будет занят/заблокирован по параллельному запросу.
Если базовая база данных драйверы (MySql?) предлагает средство для перехвата создания сокета (см. SocketFactory), тогда я предполагаю, что можно было бы построить асинхронный управляемый событиями слой базы данных поверх api JDBC, но нам пришлось бы инкапсулировать весь JDBC за фасадом, управляемым событиями, и этот фасад не будет выглядеть как JDBC (после того, как он будет управляться событиями). Обработка базы данных будет происходить асинхронно в другом потоке для вызывающего объекта, и вам нужно будет решить, как создать диспетчер транзакций, который не работает полагайтесь на сходство потоков.
Что-то вроде подхода, который я упоминаю, позволит даже одному фоновому потоку обрабатывать нагрузку параллельных JDBC exec. на практике вы, вероятно, запустите пул потоков, чтобы использовать несколько ядер.
(конечно, я не комментирую логику исходного вопроса только ответы, которые подразумевают, что параллелизм в сценарии с блокировкой сокета IO возможен без пользователя шаблона селектора-проще просто работать ваш типичный параллелизм JDBC и поместите в пул соединений нужного размера).
похоже, MySql, вероятно, делает что-то вроде того, что я предлагаю --- http://code.google.com/p/async-mysql-connector/wiki/UsageExample
невозможно сделать асинхронный вызов базы данных через JDBC, но вы можете сделать асинхронные вызовы в JDBC С актеры (например, актер делает звонки в БД через JDBC и отправляет сообщения третьим лицам, когда вызовы закончились), или, если вам нравится CPS, с конвейерные фьючерсы (обещания) (хорошая реализация Scalazобещания)
I не считайте, что использование пула потоков является решением, потому что оно не масштабируется, в случае тяжелых параллельных запросов это приведет к появлению очень большого количества потоков.
актеры Scala по умолчанию основаны на событиях (а не на потоках)-планирование продолжения позволяет создавать миллионы актеров на стандартной настройке JVM.
Если вы нацелены на Java, Akka Framework это реализация модели актора, которая имеет хороший API как для Java, так и для Скала.
кроме того, синхронная природа JDBC имеет для меня идеальный смысл. Стоимость сеанса базы данных намного выше, чем стоимость потока Java, который блокируется (либо в фоновом режиме) и ждет ответа. Если ваши запросы выполняются так долго, что возможности службы исполнителя (или оболочки Actor / fork-join / promise concurrency Framework) недостаточно для вас (и вы потребляете слишком много потоков), вы должны прежде всего подумать о загрузке базы данных. Обычно ответ из базы данных возвращается очень быстро, и служба исполнителя, поддерживаемая фиксированным пулом потоков, является достаточно хорошим решением. Если у вас слишком много длительных запросов, вы должны рассмотреть предварительную (предварительную)обработку-например, ночной пересчет данных или что-то в этом роде.
возможно, вы могли бы использовать асинхронную систему обмена сообщениями JMS, которая довольно хорошо масштабируется, IMHO:
Отправить сообщение в очередь, где подписчики примут сообщение, и запустить процесс SQL. Вашей основной процесс будет продолжать работать и принимать или отправлять новые запросы.
когда процесс SQL заканчивается, вы можете запустить обратный путь: отправить сообщение ResponseQueue с результатом процесса и слушателем на клиенте сторона принимает его и выполняет код обратного вызова.
в JDBC нет прямой поддержки, но у вас есть несколько вариантов, таких как MDB, исполнители из Java 5.
" Я не считаю, что использование пула потоков является решением, потому что оно не масштабируется, в случае тяжелых параллельных запросов это приведет к появлению очень большого количества потоков."
Мне любопытно, почему ограниченный пул потоков не будет масштабироваться? Это пул не поток на запрос, чтобы создать поток на каждый запрос. Я использую это для довольно когда-то на тяжелом веб-приложении нагрузки, и мы не видели никаких проблем до сих пор.
The Java 5.0 executors может пригодиться.
вы можете иметь фиксированное количество потоков для обработки длительных операций. И вместо
Runnable
можно использоватьCallable
, которые возвращают результат. Результат инкапсулируется вFuture<ReturnType>
проект Ajdbc, похоже, отвечает на эту проблему http://code.google.com/p/adbcj/
в настоящее время существует 2 экспериментальных изначально асинхронных драйвера для mysql и postgresql.
старый вопрос, но еще немного информации. Невозможно, чтобы JDBC выдавал асинхронные запросы к самой базе данных, если поставщик не предоставляет расширение для JDBC и оболочку для обработки JDBC. Тем не менее, можно обернуть сам JDBC с очередью обработки и реализовать логику, которая может обрабатывать очередь на одном или нескольких отдельных соединениях. Одним из преимуществ этого для некоторых типов вызовов является то, что логика, если под достаточно большой нагрузкой, может преобразовать вызовы в пакеты JDBC для обработки, что может значительно ускорить логику. Это наиболее полезно для вызовов, в которые вставляются данные, и фактический результат должен регистрироваться только в случае ошибки. Отличный пример этого - если вставки выполняются для регистрации активности пользователя. Приложение не будет заботиться, если вызов завершается немедленно или через несколько секунд.
Как Примечание стороны, один продукт на рынке обеспечивает политику подхода к возможности асинхронного вызовы, подобные тем, которые я описал, должны выполняться асинхронно (http://www.heimdalldata.com/). Отказ от ответственности: я являюсь соучредителем этой компании. Он позволяет применять регулярные выражения к запросам преобразования данных, таким как вставка/обновление/удаление для любого источника данных JDBC, и автоматически пакует их вместе для обработки. При использовании с MySQL и опцией rewriteBatchedStatements (MySQL и JDBC с rewriteBatchedStatements=true) это может существенно снижение общей нагрузки на базу данных.
У вас есть три варианта на мой взгляд:
- использовать параллельной очереди для распределения сообщений по небольшому и фиксированному количеству потоков. Поэтому, если у вас есть 1000 соединений, у вас будет 4 потока, а не 1000 потоков.
- сделайте доступ к базе данных на другом узле (т. е. другом процессе или машине) и попросите вашего клиента базы данных сделать асинхронные сетевые звонки к этому узлу.
- реализация распределенной системы через асинхронное сообщение. Для этого вам понадобится очередь сообщений, таких как CoralMQ или Tibco.
Diclaimer: я один из разработчиков CoralMQ.
просто сумасшедшая идея : вы можете использовать шаблон Iteratee над jbdc resultSet, завернутый в какое-то будущее/обещание
Хаммерсмит делает это для MongoDB.
Я просто думаю идеи здесь. Почему нельзя есть пул подключений к базе данных в поток. Каждый поток имеет доступ к очереди. Когда вы хотите сделать запрос, который занимает много времени, вы можете поставить в очередь, а затем один из потоков возьмет его и обработает. У вас никогда не будет слишком много потоков, потому что количество ваших потоков ограничено.
Edit: или еще лучше, просто несколько потоков. Когда поток видит что-то в очереди, он запрашивает соединение из пула и обрабатывает его.
библиотека commons-dbutils поддерживает
AsyncQueryRunner
который вы предоставляетеExecutorService
to и возвращает aFuture
. Стоит проверить, как это просто в использовании и убедитесь, что вы не будете утечка ресурсов.
вот набросок о том, как может выглядеть неблокирующий api jdbc от Oracle, представленный в JavaOne: https://static.rainfocus.com/oracle/oow16/sess/1461693351182001EmRq/ppt/CONF1578%2020160916.pdf
таким образом, кажется, что в конце концов, действительно асинхронные вызовы JDBC действительно будут возможны.
Если вы заинтересованы в асинхронных API базы данных для Java, вы должны знать, что есть новая инициатива, чтобы придумать набор стандартных API, основанных на CompletableFuture и lambdas. Существует также реализация этих API через JDBC, которые могут быть использованы для практики этих API: https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ Javadoc является упоминалось в README на GitHub проекта.
Как упоминалось в других ответах JDBC API не является асинхронным по своей природе.
Однако, если вы можете жить с подмножеством операций и другим API, есть решения. Одним из примеров является https://github.com/jasync-sql/jasync-sql это работает для MySQL и PostgreSQL.