RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Глава 8. Объединение связи в Connector/J

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

Как объединение связи работает

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

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

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

Выгода объединения связи

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

  • Уменьшенное время создания связи.

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

  • Упрощенная модель программирования.

    Используя объединение связи, каждый отдельный поток может действовать, как будто это создало свою собственную связь JDBC, позволив вам использовать прямые методы программирования JDBC.

  • Использование ресурсов грамотно.

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

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

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

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

Пример 8.1. 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 Data Source
     *
     * 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;
      }
    }
  }
}

Как показано в примере выше, после получения JNDI InitialContext и поиска DataSource остальная часть кода следует знакомым соглашениям JDBC.

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

Калибровка пула связи

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

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

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

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

Утверждение связей

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

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

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

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

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

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

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

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

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, но НЕ будут преобразованы в пинг. Далее для уравновешенных по нагрузке связей, запрос будет выполнен для одной связи во внутреннем пуле, вместо того, чтобы проверить каждую основную физическую связь. Это приводит к неактивным физическим связям, принимающим несвежее состояние. Если Connector/J позже повторно балансирует, он мог бы выбрать неактивную связь, приводящую к исключению, передаваемому к приложению. Чтобы помочь предотвратить это, можно использовать loadBalanceValidateConnectionOnSwapServer, чтобы проверить связь перед использованием.

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

Поиск

 

Найди своих коллег!

Вы можете направить письмо администратору этой странички, Алексею Паутову. mailto:alexey.v.pautov@mail.ru