Java Spring jdbcTemplate queryForList медленно

Я использую Spring jdbcTemplate для выполнения запросов MySQL в моем приложении Java. Это моя функция:

public static ArrayList<Map<String, Object>> query(String q) throws Exception {

    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

    @SuppressWarnings("unchecked")
    List<Map<String, Object>> result = jdbcTemplate.queryForList(q);

    return (ArrayList<Map<String, Object>>) result;

}

В этом конкретном случае я передаю функции этот запрос:

"SELECT * FROM test WHERE name IN('string1', 'string2', 'string3', ...)";

В таблице test всего 6 столбцов, строки в IN варьируются от нескольких до 100 символов. jdbcTemplate.queryForList() для выполнения запроса требуется 280 миллисекунд. ПРИМЕЧАНИЕ: для запуска jdbcTemplate.queryForList() требуется 280 миллисекунд, а не для всей функции query. Когда я запускаю точно такой же запрос на клиенте MySQL (HeidiSQL), это занимает всего 16 миллисекунд. ОБНОВЛЕНИЕ: Хайди поддерживает открытое соединение, так что это некорректное сравнение.

Вопрос: почему jdbcTemplate.queryForList() так ужасно медленен по сравнению с тем же запросом, выполняемым на клиенте MySQL? Я использую его исключительно из соображений удобства, он может получить результат в ArrayList<Map<String, Object>>, что мне и нужно. Могу ли я что-нибудь сделать, чтобы ускорить это, или мне просто бросить jdbcTemplate все вместе и использовать что-нибудь еще?

ОБНОВЛЕНИЕ: Итак, я изменил свою функцию с jdbcTemplate на использование простого jdbc. Вот так выглядит эта мерзость. Это причина, по которой я в первую очередь выбрал jdbcTemplate. Теперь для выполнения того же запроса требуется 200 миллисекунд - небольшое улучшение, но все еще слишком медленно ...

public static ArrayList<Map<String,Object>> query(String Full_Command) {

    try {

        String URL = "jdbc:mysql://localhost/dbname";
        String USER = "root";
        String PASS = "";
        java.sql.Connection Con = DriverManager.getConnection(URL, USER, PASS);

        //create statement
        Statement Stm = null;
        //Stm = Con.createStatement();
        Stm = (Statement) Con.createStatement();

        //query
        ResultSet Result = null;
        boolean Returning_Rows = Stm.execute(Full_Command);
        if (Returning_Rows) {
            Result = Stm.getResultSet();
        } else {
            return new ArrayList<Map<String,Object>>();
        }

        //get metadata
        ResultSetMetaData Meta = null;
        Meta = Result.getMetaData();

        //get column names
        int Col_Count = Meta.getColumnCount();
        ArrayList<String> Cols = new ArrayList<String>();
        for (int Index=1; Index<=Col_Count; Index++) {
            Cols.add(Meta.getColumnName(Index));
        }

        //fetch out rows
        ArrayList<Map<String, Object>> Rows = new ArrayList<Map<String,Object>>();

        while (Result.next()) {
            HashMap<String,Object> Row = new HashMap<String,Object>();
            for (String Col_Name:Cols) {
                Object Val = Result.getObject(Col_Name);
                Row.put(Col_Name,Val);
            }
            Rows.add(Row);
        }

        //close statement
        Stm.close();

        //pass back rows
        return Rows;

    } catch (Exception Ex) {

        System.out.print(Ex.getMessage());
        return new ArrayList<Map<String,Object>>();

    }

}

ОБНОВЛЕНИЕ 2: я не разбивал функцию JDBC на время выполнения, и эта единственная строка является узким местом, каждый раз занимая ~ 190 миллисекунд:

java.sql.Connection Con = DriverManager.getConnection(URL, USER, PASS);

Любые комментарии?


person Caballero    schedule 06.08.2013    source источник
comment
Не могли бы вы показать нам определение bean-компонента источника данных Spring?   -  person Qwerky    schedule 06.08.2013


Ответы (4)


Это не вина JdbcTemplate. Вот почему:

Прежде всего, вы должны снизить накладные расходы на установление соединения. Эти накладные расходы могут достигать 500 миллисекунд. Это нормально.

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

В-третьих, индексируете ли вы столбец name?

В-четвертых, если накладные расходы на соединение действительно так важны для вас, вы можете подумать о сохранении соединения или даже о создании пула соединений.

person ktm5124    schedule 06.08.2013
comment
Я уже сузил его до накладных расходов на подключение. Хотя это java-приложение типа lanuch-do-the-вещь-and-close, оно не запускается все время, поэтому будет ли в этом случае работать сохранение или объединение соединений? - person Caballero; 06.08.2013
comment
Тогда пул соединений кажется излишним. Все, что вам нужно сделать, это установить соединение при запуске приложения, а затем повторно использовать это соединение для всех последующих запросов. Поймите, что для установления соединения требуется время, поэтому вам придется иметь дело с ожиданием в 200 мс в начале вашей программы (что в любом случае почти незаметно). - person ktm5124; 06.08.2013
comment
Я понимаю. В любом случае это конкретное приложение выполняет только один запрос. Но я не понимаю, как PHP может устанавливать соединение, выполнять полдюжины запросов и генерировать страницу менее чем за 100 мс, а Java даже не может установить соединение с базой данных одновременно. - person Caballero; 06.08.2013
comment
Это не вина Java. Я очень сомневаюсь, что PHP может лучше устанавливать соединения, чем Java. Я считаю, что клиент MySQL занимает примерно столько же времени. Я был бы уверен в ваших тестах, прежде чем исследовать это утверждение. Вы также должны спросить себя, использует ли скрипт PHP постоянные соединения? он подключается к той же базе данных? он уже находится на машине, на которой размещена база данных? Есть много мелочей, которые могут объяснить разницу. - person ktm5124; 06.08.2013
comment
PHP в любом случае не делает пул соединений невидимым для вас? - person Brian; 06.08.2013
comment
@ Брайан, я подозреваю, что да. Вот почему это так быстро. Мне нужно как-то сделать то же самое для java. - person Caballero; 07.08.2013
comment
Apache Commons DBCP предоставляет это, просто нужна небольшая явная конфигурация. Также отлично работает со Spring, с ним очень легко работать. - person Brian; 07.08.2013
comment
@Brian Так уж случилось, что я пытаюсь реализовать Apache Commons для этого, но он не работает: stackoverflow.com/questions/18103852/ - person Caballero; 07.08.2013

Попробуйте увеличить размер выборки (JdbcTemplate.setFetchSize), это может быть узким местом.

person Evgeniy Dorofeev    schedule 06.08.2013
comment
Что такое размер выборки? Неважно, нашел - это сеттинг. - person Caballero; 06.08.2013
comment
Я увеличил размер выборки до 1000000000, но никаких изменений. - person Caballero; 06.08.2013
comment
размер выборки - это число, если драйвер строк читает из БД за один раз - person Evgeniy Dorofeev; 06.08.2013
comment
Хорошо, тогда я предлагаю попробовать чистый JDBC и посмотреть, есть ли разница в производительности - person Evgeniy Dorofeev; 06.08.2013

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

person Lokesh    schedule 06.08.2013
comment
Да, это была первая попытка с клиентом MySQL, определенно без кеширования. Что вы имеете в виду под журналами Java? - person Caballero; 06.08.2013

Могут быть накладные расходы на установление вашего первого подключения к БД. Попробуйте протестировать производительность после того, как у вас уже будет соединение.

Этот материал не должен быть медленным, выгода где-то там!

person Brian    schedule 06.08.2013
comment
Определенно не накладные расходы на подключение, я это уже отмечал. Я только проверил время выполнения запроса. - person Caballero; 06.08.2013
comment
Посмотрите мое последнее обновление, нашел одну строку, которая вызывает узкое место. - person Caballero; 06.08.2013
comment
Итак, это медленное соединение get ...? Эта строка устанавливает соединение между вашим приложением и MySQL, и соединение обычно довольно медленное. Обычно по этой причине приложения будут использовать пулы соединений, такие как Apache Commons DBCP (есть и другие). - person Brian; 06.08.2013
comment
Да, в первый раз я запутался, потому что думал, что соединение было инициировано путем создания jdbcTemplate, а не при выполнении фактического запроса. - person Caballero; 06.08.2013
comment
Кстати, немного удивлен, что вы приняли ответ от кого-то, кто ответил после того, как мы установили, что проблема связана с подключением к БД? - person Brian; 07.08.2013