Возможен ли асинхронный вызов jdbc?


158

Интересно, есть ли способ совершать асинхронные вызовы к базе данных?

Например, представьте, что у меня большой запрос, который обрабатывается очень долго, я хочу отправить запрос и получить уведомление, когда запрос вернет значение (путем передачи Listener / callback или чего-то еще). Я не хочу блокировать ожидание ответа базы данных.

Я не считаю, что использование пула потоков - это решение, потому что оно не масштабируется, в случае тяжелых параллельных запросов это породит очень большое количество потоков.

Мы сталкиваемся с такого рода проблемами с сетевыми серверами, и мы нашли решения, используя системный вызов select / poll / epoll, чтобы избежать использования одного потока на соединение. Мне просто интересно, как иметь подобную функцию с запросом к базе данных?

Примечание: я знаю, что использование FixedThreadPool может быть хорошим обходным путем, но я удивлен, что никто не разработал систему действительно асинхронную (без использования дополнительного потока).

** Обновление **
Из-за отсутствия реальных практических решений я решил сам создать библиотеку (часть finagle): finagle-mysql . Он в основном декодирует / декодирует запрос / ответ mysql и использует Finagle / Netty под капотом. Он отлично масштабируется даже при огромном количестве соединений.




Проблема в том, как БД может уведомить клиента о завершении запроса. Можно было бы (например), чтобы Oracle использовал функцию «Уведомление об изменении результата запроса базы данных» и получал уведомление при изменении данных в БД. Это относится к запросам SQL, которые изменяют данные базы данных. Для запросов только для чтения это не будет работать. С другой стороны, я не уверен, что создание асинхронных соединений было бы хорошей идеей, поскольку их установка обходится дорого. Конечно, это не очень общее решение. Просто пища для размышлений ...
Майк Аргириу,

Finagle-mysql использует JDBC?
Саид Заринфам

Ответы:


164

Я не понимаю, как какой-либо из предложенных подходов, которые заключают в себе вызовы JDBC в Actors, executors или чем-то еще, может помочь здесь - может кто-то прояснить.

Конечно, основная проблема заключается в том, что операции JDBC блокируются на сокете ввода-вывода. Когда он делает это, он блокирует поток, продолжающий историю. Независимо от того, какую оболочку вы выберете, вы будете использовать один поток, который будет занят / заблокирован для одновременного запроса.

Если базовые драйверы базы данных (MySql?) Предлагают средства для перехвата создания сокета (см. SocketFactory), то я полагаю, что было бы возможно создать асинхронный управляемый событиями слой базы данных поверх API JDBC, но нам пришлось бы инкапсулировать весь JDBC за фасадом, управляемым событиями, и этот фасад не будет выглядеть как JDBC (после того, как он будет управляться событиями). Обработка базы данных будет происходить асинхронно в другом потоке к вызывающей стороне, и вам нужно будет решить, как создать диспетчер транзакций, который не зависит от привязки потока.

Нечто подобное описанному мною подходу позволило бы даже одному фоновому потоку обрабатывать загрузку одновременных JDBC-исполняемых файлов. На практике вы, вероятно, запустите пул потоков, чтобы использовать несколько ядер.

(Конечно, я не комментирую логику исходного вопроса, а просто ответы, которые подразумевают, что параллелизм в сценарии с блокировкой ввода-вывода сокетов возможен без пользователя шаблона селектора - проще просто проработать свой типичный JDBC-параллелизм и поместить в пуле соединений нужного размера).


Похоже, что MySql, вероятно, делает что-то вроде того, что я предлагаю --- http://code.google.com/p/async-mysql-connector/wiki/UsageExample


1
Использование Akka не делает вызовы реляционных БД асинхронными. Это позволяет вам легко запускать их на нескольких выделенных потоках для доступа к БД. Таким образом, вы не отключаете весь сайт, когда он перестает отвечать на запросы, потому что вы всегда выполняли асинхронные вызовы на уровне обслуживания для уровня DAO с обещаниями, а потоки вашего веб-сервера отделены от остальной части вашего приложения.
Онур

Актеры - не единственные обходные пути (например, микро-сервисы и асинхронный http, который мы масштабируем до тысяч в секунду), и я бы не стал так быстро отклонять их как не асинхронные с точки зрения клиента. Если трафик потоков 1k UI поступает в вашу систему и только 10 потоков блокируются в БД, тогда как 990 «сообщений» (или что-то подобное) помещаются в очередь в памяти без блокировки какого-либо из потоков 1k UI (которые, вероятно, будут освобождены). .. разве это не то, что требуется? Я хотел бы видеть истинный асинхронный JDBC, но это не значит, что в промежуточный период не существует чрезвычайно жизнеспособных обходных путей.
Грег Пендлбери

42

Невозможно сделать асинхронный вызов к базе данных через JDBC, но вы можете делать асинхронные вызовы в JDBC с помощью Actors (например, actor делает вызовы в DB через JDBC и отправляет сообщения третьим сторонам, когда вызовы завершены), или, если вам нравится CPS, с конвейерными фьючерсами (обещаниями) (хорошая реализация - Scalaz Promises )

Я не считаю, что использование пула потоков - это решение, потому что оно не масштабируется, в случае тяжелых параллельных запросов это породит очень большое количество потоков.

Акторы Scala по умолчанию основаны на событиях (а не на потоках) - планирование продолжения позволяет создавать миллионы акторов при стандартной настройке JVM.

Если вы ориентируетесь на Java, Akka Framework - это реализация модели Actor, которая имеет хороший API как для Java, так и для Scala.


Кроме того, синхронный характер JDBC имеет для меня прекрасный смысл. Стоимость сеанса базы данных намного выше, чем стоимость блокировки потока Java (в фоновом или фоновом режиме) и ожидания ответа. Если ваши запросы выполняются так долго, что возможностей службы-исполнителя (или обертывания сред параллелизма Actor / fork-join / обещание) вам недостаточно (и вы используете слишком много потоков), вам следует прежде всего подумать о своем загрузка базы данных. Обычно ответ из базы данных возвращается очень быстро, и служба исполнителя, поддерживаемая фиксированным пулом потоков, является достаточно хорошим решением. Если у вас слишком много долго выполняющихся запросов, вам следует рассмотреть предварительную (предварительную) обработку - например, ночной пересчет данных или что-то в этом роде.


2
@Victor, каждый актер, работающий параллельно над операцией блокировки (JDBC), будет работать в отдельном потоке, которого Стив пытается избежать
Васил Ременюк

36
Подход субъекта по-прежнему требует одного потока на активную транзакцию базы данных, пока транзакция продолжается, так что это на самом деле не является решением проблемы OP, если вы не хотите ограничить число параллельных транзакций базы данных и ожидать некоторых «асинхронных» операций с базой данных. для некоторых уже исполняющихся, чтобы закончить и освободить поток. Однако это неплохая идея - база данных может быть перегружена, если вы откроете слишком много подключений, поэтому размещение транзакции базы данных в очереди для обработки вместо блокировки потока обработки http-запроса поможет.
Добес Вандермер

8
Решение на основе акторов все еще блокирует поток. Не говорите, что невозможно выполнить асинхронный вызов jdbc, есть экспериментальные библиотеки с открытым исходным кодом, которые пытаются реализовать асинхронный jdbc.

6
+1 «Стоимость сеанса базы данных намного выше, чем стоимость блокируемого потока Java»
Пол Дрейпер,

1
Для дорогих вызовов в БД проблема обычно не такая уж большая. Когда вызов является тривиальным, сетевые издержки становятся проблемой. Если вы хотите выполнить 100 запросов, каждый из которых занимает 1 мс для БД, но нагрузка на сеть составляет 200 мс, то синхронно это займет более 20 секунд, но асинхронно займет 300 мс.
умереть

12

Возможно, вы могли бы использовать систему асинхронного обмена сообщениями JMS, которая хорошо масштабируется, IMHO:

  • Отправьте сообщение в очередь, где подписчики примут сообщение, и запустите процесс SQL. Ваш основной процесс будет продолжать работать и принимать или отправлять новые запросы.

  • Когда процесс SQL завершается, вы можете запустить противоположный путь: отправить сообщение в ResponseQueue с результатом процесса, а слушатель на стороне клиента примет его и выполнит код обратного вызова.


7

В JDBC нет прямой поддержки, но у вас есть несколько вариантов, таких как MDB, Executors from Java 5.

«Я не считаю, что использование пула потоков - это решение, потому что оно не масштабируется, в случае тяжелых параллельных запросов это породит очень большое количество потоков».

Мне интересно, почему ограниченный пул потоков не собирается масштабироваться? Это пул, а не поток по запросу, порождающий поток для каждого запроса. Я использовал это в течение довольно долгого времени в веб-приложении с большой нагрузкой, и мы пока не видели никаких проблем.


Я думаю, что основной аргумент против потоков заключается в том, что вы в основном находитесь за пределами каких-либо стандартных ограничений контейнеров Java, поэтому вы теряете управляемую контейнером кластеризацию и отказоустойчивые возможности, хотя вы можете свернуть свои собственные или использовать что-то вроде терракоты.
Мезмо

3
мы можем подключиться к опросам, управляемым сервером приложений, используя менеджеры работ. websphere, weblogic и glassfish поддерживают это
Аравинд Яррам


4

Как уже упоминалось в других ответах, JDBC API не является асинхронным по своей природе.
Однако, если вы можете жить с подмножеством операций и другим API, есть решения. Одним из примеров является https://github.com/jasync-sql/jasync-sql, который работает для MySQL и PostgreSQL.


3

Проект Ajdbc, кажется, решает эту проблему http://code.google.com/p/adbcj/

В настоящее время есть 2 экспериментальных асинхронных драйвера для mysql и postgresql.


Я хотел бы, чтобы этот подход был готов. JDBC с самого начала сильно развивался (итераторы, шаблоны, подготовленные процедуры), но этот асинхронный подход никогда не был реализован. Это было бы особенно интересно для операций записи (Вставка, Обновление, Удаление), и особенно тех тяжелых TX, с которыми мы все сталкиваемся. По моему мнению, любой вид клиентского подхода (объединение в пул, актер, планирование, обмен сообщениями ...) приведет к небольшому вознаграждению с точки зрения использования ресурсов (возможно, некоторый выигрыш в пропускной способности или задержке).
Хайме Касеро

Старые и заброшенные, поддерживаются только два типа данных и даже не готовы к работе. К сожалению :(
Аарон Зинман

Вопрос № 1 этой библиотеки о недоступности веб-сайта . Ему больше года. Я подозреваю, что эта библиотека довольно мертва.
Лукас Эдер

3

Старый вопрос, но еще немного информации. Невозможно, чтобы JDBC выдавал асинхронные запросы к самой базе данных, если только поставщик не предоставил расширение JDBC и оболочку для обработки JDBC. Тем не менее, можно обернуть сам JDBC в очередь обработки и реализовать логику, которая может обрабатывать очередь в одном или нескольких отдельных соединениях. Одним из преимуществ этого для некоторых типов вызовов является то, что логика, если она находится под достаточно большой нагрузкой, может преобразовывать вызовы в пакеты JDBC для обработки, что может значительно ускорить логику. Это наиболее полезно для вызовов, в которые вставляются данные, а фактический результат необходимо регистрировать только в случае ошибки. Прекрасным примером этого является, если вставки выполняются для регистрации активности пользователя. Приложение выиграло

В качестве дополнительного примечания, один продукт на рынке обеспечивает политический подход к разрешению асинхронных вызовов, подобных тем, которые я описал, для асинхронного выполнения ( http://www.heimdalldata.com/ ). Отказ от ответственности: я являюсь соучредителем этой компании. Он позволяет применять регулярные выражения к запросам преобразования данных, таким как вставка / обновление / удаление для любого источника данных JDBC, и автоматически объединяет их вместе для обработки. При использовании с MySQL и опцией rewriteBatchedStatements ( MySQL и JDBC с rewriteBatchedStatements = true ) это может значительно снизить общую нагрузку на базу данных.


Но это все еще означает, что JDBC должен иметь хотя бы один отдельный поток. Как насчет каркасов и стеков, которые являются однопоточными, но все еще основаны на обратном вызове (на ум приходит nodejs)? Вы знаете, как они управляют вызовами JDBC?
Юранос

3

У вас есть три варианта на мой взгляд:

  1. Используйте параллельную очередь для распределения сообщений по небольшому и фиксированному количеству потоков. Таким образом, если у вас 1000 соединений, у вас будет 4 потока, а не 1000 потоков.
  2. Получите доступ к базе данных на другом узле (то есть другом процессе или машине) и попросите клиента базы данных выполнить асинхронные сетевые вызовы с этим узлом.
  3. Реализуйте настоящую распределенную систему с помощью асинхронных сообщений. Для этого вам понадобится очередь сообщений, такая как CoralMQ или Tibco.

Диклаймер: Я один из разработчиков CoralMQ.


3

Разрабатывается решение для обеспечения возможности реактивного подключения со стандартными реляционными базами данных.

Люди, желающие масштабировать при сохранении использования реляционных баз данных, отрезаны от реактивного программирования из-за существующих стандартов, основанных на блокировке ввода-вывода. R2DBC определяет новый API, который позволяет реактивному коду эффективно работать с реляционными базами данных.

R2DBC - это спецификация, разработанная с нуля для реактивного программирования с базами данных SQL, определяющая неблокирующий SPI для разработчиков драйверов баз данных и авторов клиентских библиотек. Драйверы R2DBC полностью реализуют проводной протокол базы данных поверх неблокирующего уровня ввода-вывода.

Сайт R2DBC

GitHub R2DBC

Функциональная матрица

введите описание изображения здесь


2

Исполнители Java 5.0 могут пригодиться.

Вы можете иметь фиксированное количество потоков для обработки длительных операций. И вместо Runnableвас можно использовать Callable, которые возвращают результат. Результат инкапсулирован в Future<ReturnType>объект, так что вы можете получить его, когда он вернется.


2

Вот краткое описание того, как неблокируемый API jdbc может выглядеть из Oracle, представленного на JavaOne: https://static.rainfocus.com/oracle/oow16/sess/1461693351182001EmRq/ppt/CONF1578%2020160916.pdf

Таким образом, кажется, что в конечном итоге действительно асинхронные вызовы JDBC будут действительно возможны.


Это не JDBC, а дополнительный API
yaccob

2

Просто сумасшедшая идея: вы можете использовать шаблон Iteratee над JBDC resultSet, завернутый в некое Future / Promise

Хаммерсмит делает это для MongoDB .


1

Я просто думаю об идеях здесь. Почему вы не можете иметь пул соединений с базой данных, каждый из которых имеет поток. Каждый поток имеет доступ к очереди. Если вы хотите выполнить запрос, который занимает много времени, вы можете поставить его в очередь, и тогда один из потоков подберет его и обработает. У вас никогда не будет слишком много потоков, потому что количество ваших потоков ограничено.

Изменить: Или еще лучше, просто несколько потоков. Когда поток видит что-то в очереди, он запрашивает соединение из пула и обрабатывает его.


1

Библиотека commons-dbutils имеет поддержку для AsyncQueryRunnerкоторой вы предоставляете, ExecutorServiceи она возвращает Future. Стоит проверить, так как он прост в использовании и гарантирует, что вы не потеряете ресурсы.


1

Если вас интересуют API-интерфейсы для асинхронных баз данных для Java, вы должны знать, что появилась новая инициатива по разработке набора стандартных API-интерфейсов, основанных на CompletableFuture и lambdas. Существует также реализация этих API через JDBC, которую можно использовать для практики этих API: https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ . JavaDoc упоминается в README проект GitHub.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.