Spec-Zone .ru
спецификации, руководства, описания, API

21.3.7. Объединение в пул соединения с Connector/J

Объединение в пул соединения является методом создания и управления пулом соединений, которые готовы к употреблению любым потоком, который нуждается в них. Объединение в пул соединения может значительно увеличить производительность Вашего приложения Java, уменьшая полное использование ресурсов.

Как Объединение в пул Соединения Работает

Большинство приложений только нуждается в потоке, чтобы иметь доступ к соединению JDBC, когда они активно обрабатывают транзакцию, которая часто берет только миллисекунды, чтобы завершиться. Если не обрабатывая транзакцию, соединение простаивает. Объединение в пул соединения позволяет неактивному соединению использоваться некоторым другим потоком, чтобы сделать полезную работу.

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

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

Преимущества Объединения в пул Соединения

Основные преимущества к объединению в пул соединения:

Используя Объединение в пул Соединения с Connector/J

Sun стандартизировал понятие объединения в пул соединения в JDBC через JDBC 2.0 Дополнительных интерфейса, и у всех главных серверов приложений есть реализации этих API, которые работают с MySQL, Connector/J.

Обычно, Вы конфигурируете пул соединения в своих конфигурационных файлах сервера приложений, и получаете доступ к нему через Интерфейс Именования и Каталога Java (JNDI). Следующий код показывает, как Вы могли бы использовать пул соединения из приложения, развернутого в сервере приложений J2EE:

Пример 21.11. Connector/J: Используя соединение объединяют в пул с сервером приложений J2EE

import java.sql.Connection;import java.sql.SQLException;import java.sql.Statement;import javax.naming.InitialContext;import javax.sql.DataSource;public class MyServletJspOrEjb {    public void doSomething() throws Exception {        /*         * Create a JNDI Initial context to be able to         *  lookup  the DataSource         *         * In production-level code, this should be cached as         * an instance or static variable, as it can         * be quite expensive to create a JNDI context.         *         * Note: This code only works when you are using servlets         * or EJBs in a J2EE application server. If you are         * using connection pooling in standalone Java code, you         * will have to create/configure datasources using whatever         * mechanisms your particular connection pooling library         * provides.         */        InitialContext ctx = new InitialContext();         /*          * Lookup the DataSource, which will be backed by a pool          * that the application server provides. DataSource instances          * are also a good candidate for caching as an instance          * variable, as JNDI lookups can be expensive as well.          */        DataSource ds =          (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");        /*         * The following code is what would actually be in your         * Servlet, JSP or EJB 'service' method...where you need         * to work with a JDBC connection.         */        Connection conn = null;        Statement stmt = null;        try {            conn = ds.getConnection();            /*             * Now, use normal JDBC programming to work with             * MySQL, making sure to close each resource when you're             * finished with it, which permits the connection pool             * resources to be recovered as quickly as possible             */            stmt = conn.createStatement();            stmt.execute("SOME SQL QUERY");            stmt.close();            stmt = null;            conn.close();            conn = null;        } finally {            /*             * close any jdbc instances here that weren't             * explicitly closed during normal code path, so             * that we don't 'leak' resources...             */            if (stmt != null) {                try {                    stmt.close();                } catch (sqlexception sqlex) {                    // ignore, as we can't do anything about it here                }                stmt = null;            }            if (conn != null) {                try {                    conn.close();                } catch (sqlexception sqlex) {                    // ignore, as we can't do anything about it here                }                conn = null;            }        }    }}

При использовании объединения в пул соединения всегда удостоверяйтесь, что закрываются соединения, и что-либо создаваемое ими (такие как операторы или наборы результатов). Это правило применяет независимо от того, что происходит в Вашем коде (исключения, поток управления, и т.д). Когда эти объекты закрываются, они могут быть снова использованы; иначе, они будут скручены, что означает, что ресурсы сервера MySQL, которые они представляют (такие как буферы, блокировки, или сокеты) связываются в течение некоторого времени, или в худшем случае может быть связан навсегда.

Калибровка Пула Соединения

У каждого соединения с MySQL есть издержки (память, ЦП, контекстные переключения, и т.д) на обоих сторона клиента и сервера. Каждое соединение ограничивает, сколько ресурсов там доступно Вашему приложению так же как серверу MySQL. Многие из этих ресурсов будут использоваться, делает ли соединение фактически какую-либо полезную работу! Пулы соединения могут быть настроены, чтобы максимизировать производительность, сохраняя использование ресурса ниже точки, где Ваше приложение начнет перестать работать, а не только работать медленнее.

Оптимальный размер для пула соединения зависит от ожидаемой загрузки и среднее время транзакции базы данных. Практически, оптимальный размер пула соединения может быть меньшим, чем Вы могли бы ожидать. Если Вы берете Java Sun приложение проекта Petstore например, пул соединения 15-20 соединений может служить относительно умеренной загрузке (600 параллельных пользователей) использование MySQL и Tomcat с приемлемым временем отклика.

Чтобы правильно измерить пул соединения для Вашего приложения, создайте сценарии нагрузочного теста с инструментами, такими как Apache JMeter или Шлифовальный станок, и нагрузочный тест Ваше приложение.

Легкий способ определить начальную точку состоит в том, чтобы сконфигурировать Ваше максимальное количество пула соединения соединений, чтобы быть неограниченным, выполнить нагрузочный тест, и измерить самое большое количество одновременно используемых соединений. Можно тогда работать назад оттуда, чтобы определить то, что значения минимальных и максимальных объединенных в пул соединений дают лучшей производительности для Вашего определенного приложения.

Проверка допустимости Соединений

MySQL, Connector/J, может проверить соединения, выполняя легкий ping против сервера. В случае сбалансированных с загрузки соединений это выполняется против всех активных объединенных в пул внутренних соединений, которые сохраняются. Это выгодно для приложений Java, используя пулы соединения, поскольку пул может использовать эту функцию, чтобы проверить соединений. В зависимости от Вашего пула соединения и конфигурации, эта проверка допустимости может быть выполнена в разное время:

  1. Прежде, чем пул возвращает соединение с приложением.

  2. Когда приложение возвращает соединение с пулом.

  3. Во время периодических проверок неактивных соединений.

Чтобы использовать эту функцию, определите запрос проверки допустимости в своем пуле соединения, который запускается с /* ping */. Отметьте, что синтаксис должен быть точно как определен. Это вызовет драйвер, отправляют ping серверу и возвращают фиктивный легкий набор результатов. При использовании a ReplicationConnection или LoadBalancedConnection, ping будет отправлен через все активные соединения.

Является критическим, что синтаксис определяется правильно. Синтаксис должен быть точным по причинам эффективности, поскольку этот тест делается для каждого оператора, который выполняется:

protected static final String PING_MARKER = "/* ping */";...if (sql.charAt(0) == '/') {if (sql.startsWith(PING_MARKER)) {doPingInstead();...

Ни один из следующих отрывков не будет работать, потому что синтаксис ping чувствителен к пробелу, написанию прописными буквами, и размещению:

sql = "/* PING */ SELECT 1";sql = "SELECT 1 /* ping*/";sql = "/*ping*/ SELECT 1";sql = " /* ping */ SELECT 1";sql = "/*to ping or not to ping*/ SELECT 1";

Все предыдущие операторы выпустят нормальное SELECT оператор и не будет преобразован в легкий ping. Далее, для сбалансированных с загрузки соединений, оператор будет выполняться против одного соединения во внутреннем пуле, вместо того, чтобы проверить каждого базового физического соединения. Это приводит к неактивным физическим соединениям, принимающим устарелое состояние, и они могут умереть. Если Connector/J тогда повторно балансируется, он мог бы выбрать мертвое соединение, приводящее к исключению, которое передают к приложению. Чтобы помочь предотвратить это, можно использовать loadBalanceValidateConnectionOnSwapServer проверить соединения перед использованием.

Если Ваше развертывание Connector/J использует пул соединения, который позволяет Вам определять запрос проверки допустимости, использовать в своих интересах его, но гарантировать, что запрос запускается точно с /* ping */. Это особенно важно, если Вы используете выравнивание нагрузки или осведомленные о репликации функции Connector/J, поскольку это поможет поддержать соединения, которые иначе пойдут устарелые и умрут, вызывая проблемы позже.