Понимание JdbcConnectionPool с базой данных H2


У меня есть серверный процесс H2, запущенный на моей основной серверной машине, разрешающий TCP-соединения.

Допустим, я хочу выполнить 100 SQL-запросов:

SELECT * FROM TEST

И, для моих собственных целей, я хочу сделать один запрос на поток. Давайте сделаем это с одним и только одним Connection объектом, общим для потоков:

  • создайте один объект Connection.
  • создайте 100 потоков.
  • в каждом потоке используйте объект shared Connection для вызова SQL-запроса.

Вышеуказанная воля работайте, но это будет немного медленно. Конечно, в конце концов, если кто-то использует Connection, то другие должны ждать его.

Ну тогда давайте сделаем по одному Connection на поток:

  • создайте 100 потоков.
  • в каждом потоке создайте новый объект Connection и вызовите SQL-запрос.
Гораздо, гораздо быстрее. Но я чувствую, что 100 соединений-это немного расточительство. Может быть, 50 соединений подойдут. Я слышал, что могу использовать JdbcConnectionPool для такого рода вещь.
  • создайте JdbcConnectionPool с максимальным числом соединений 50.
  • создайте 100 потоков.
  • в каждом потоке используйте pool.getConnection(), а затем вызовите SQL-запрос.

Ха. Это медленно. Во всяком случае, это так же медленно, как и первый подход. Может быть, из любопытства я должен установить максимум связей на 100...

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

В чем может быть проблема? Вот код для последнего теста:

import java.sql.Connection;
import java.sql.ResultSet;

import org.h2.jdbcx.JdbcConnectionPool;

public class App {

    public static void main(String[] args) throws Exception {
        Class.forName("org.h2.Driver");
        JdbcConnectionPool pool = JdbcConnectionPool.create("url", "user", "password");
        pool.setMaxConnections(100);
        for (int i = 0; i < 100; ++i) {
            Thread t = new Thread(new Client(i, pool));
            t.start();
        }
    }
}

class Client implements Runnable {
    int id;
    JdbcConnectionPool pool;
    public Client(int id, JdbcConnectionPool pool) {
        this.id = id;
        this.pool = pool;
    }
    public void run() {
        try {
            Connection conn = pool.getConnection();
            ResultSet set = conn.createStatement().executeQuery("SELECT * FROM TEST");
            if (set.next()) {
                System.out.println("Finished " + id);
            }
            set.close();
            conn.close();
        }catch (Exception e) {

        }
    }
}

Я использую H2 1.4.182.

1 4

1 ответ:

Исходный код JdbcConnectionPool#getConnection():

  public Connection getConnection() throws SQLException {
        long max = System.currentTimeMillis() + timeout * 1000;
        do {
              synchronized (this){
                if (activeConnections < maxConnections) {
                    return getConnectionNow();
                }
                try {
                    wait(1000);
                } catch (InterruptedException e) {
                    // ignore
                }
            }
        } while (System.currentTimeMillis() <= max);
        throw new SQLException("Login timeout", "08001", 8001);
    }

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

Я считаю, что встроенный пул соединений h2 довольно прост. Если вы хотите иметь высокопроизводительный пул соединений, вы можете посмотреть на C3P0 или BoneCP.