запрос намного медленнее с readyStatement по сравнению с оператором - PullRequest
1 голос
/ 07 ноября 2019

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

public String getPropertyPreparedStatement(String address) throws Exception {
    Connection conn = null;
    PreparedStatement pst = null;
    ResultSet rs = null;

    String content = null;

    try {
        Class.forName("org.postgresql.Driver");
        conn = DataSourceUtils.getConnection(template.getDataSource());

        pst = conn.prepareStatement(
                "EXPLAIN ANALYZE SELECT property.id AS property_id , full_address, street_address, street.street, city.city as city, state.state_code as state_code, zipcode.zipcode as zipcode FROM property INNER JOIN street ON street.id = property.street_id INNER JOIN city ON city.id = property.city_id INNER JOIN state ON state.id = property.state_id INNER JOIN zipcode ON zipcode.id = property.zipcode_id WHERE full_address = ?");
        pst.setString(1, address);

        rs = pst.executeQuery();

        while (rs.next()) {
            // content = rs.getString("street_address");
            System.out.println(rs.getString(1));
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (pst != null) {
            pst.close();
        }
        if (rs != null) {
            rs.close();
        }
        if (conn != null) {
            conn.close();
        }
    }

    return content;
}

ОБЪЯСНЕНИЕ АНАЛИЗА для приведенного выше метода ниже

Nested Loop  (cost=1.27..315241.91 rows=1 width=97) (actual time=0.091..688.583 rows=1 loops=1)
  ->  Nested Loop  (cost=0.98..315233.61 rows=1 width=107) (actual time=0.079..688.571 rows=1 loops=1)
        ->  Nested Loop  (cost=0.71..315225.26 rows=1 width=120) (actual time=0.069..688.561 rows=1 loops=1)
              ->  Nested Loop  (cost=0.42..315216.95 rows=1 width=127) (actual time=0.057..688.548 rows=1 loops=1)
                    ->  Seq Scan on property  (cost=0.00..315208.51 rows=1 width=131) (actual time=0.032..688.522 rows=1 loops=1)
                          Filter: ((full_address)::text = '139-Skillman-Ave-Apt-5C-Brooklyn-NY-11211'::text)
                          Rows Removed by Filter: 8790
                    ->  Index Scan using street_pkey on street  (cost=0.42..8.44 rows=1 width=28) (actual time=0.019..0.019 rows=1 loops=1)
                          Index Cond: (id = property.street_id)
              ->  Index Scan using city_id_pk on city  (cost=0.29..8.30 rows=1 width=25) (actual time=0.010..0.010 rows=1 loops=1)
                    Index Cond: (id = property.city_id)
        ->  Index Scan using state_id_pk on state  (cost=0.28..8.32 rows=1 width=19) (actual time=0.008..0.008 rows=1 loops=1)
              Index Cond: (id = property.state_id)
  ->  Index Scan using zipcode_id_pk on zipcode  (cost=0.29..8.30 rows=1 width=22) (actual time=0.010..0.010 rows=1 loops=1)
        Index Cond: (id = property.zipcode_id)
Planning Time: 2.400 ms
Execution Time: 688.674 ms

Приведенный ниже метод использует оператор, и у меня есть адрес непосредственно в запроседля проверки производительности

public String getPropertyStatement() throws Exception {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;

    String content = null;

    try {
        Class.forName("org.postgresql.Driver");
        conn = DataSourceUtils.getConnection(template.getDataSource());
        stmt = conn.createStatement();

        rs = stmt.executeQuery(
                "EXPLAIN ANALYZE SELECT property.id AS property_id , full_address, street_address, street.street, city.city as city, state.state_code as state_code, zipcode.zipcode as zipcode FROM property INNER JOIN street ON street.id = property.street_id INNER JOIN city ON city.id = property.city_id INNER JOIN state ON state.id = property.state_id INNER JOIN zipcode ON zipcode.id = property.zipcode_id WHERE full_address = '139-Skillman-Ave-Apt-5C-Brooklyn-NY-11211'");

        while (rs.next()) {
            // content = rs.getString("street_address");
            System.out.println(rs.getString(1));
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (stmt != null) {
            stmt.close();
        }
        if (rs != null) {
            rs.close();
        }
        if (conn != null) {
            conn.close();
        }
    }

    return content;
}

ОБЪЯСНИТЕ АНАЛИЗ для описанного выше метода

Nested Loop  (cost=29.82..65.96 rows=1 width=97) (actual time=0.232..0.235 rows=1 loops=1)
  ->  Nested Loop  (cost=29.53..57.65 rows=1 width=107) (actual time=0.220..0.223 rows=1 loops=1)
        ->  Nested Loop  (cost=29.25..49.30 rows=1 width=120) (actual time=0.211..0.213 rows=1 loops=1)
              ->  Nested Loop  (cost=28.97..41.00 rows=1 width=127) (actual time=0.198..0.200 rows=1 loops=1)
                    ->  Bitmap Heap Scan on property  (cost=28.54..32.56 rows=1 width=131) (actual time=0.175..0.177 rows=1 loops=1)
                          Recheck Cond: (full_address = '139-Skillman-Ave-Apt-5C-Brooklyn-NY-11211'::citext)
                          Heap Blocks: exact=1
                          ->  Bitmap Index Scan on property_full_address  (cost=0.00..28.54 rows=1 width=0) (actual time=0.162..0.162 rows=1 loops=1)
                                Index Cond: (full_address = '139-Skillman-Ave-Apt-5C-Brooklyn-NY-11211'::citext)
                    ->  Index Scan using street_pkey on street  (cost=0.42..8.44 rows=1 width=28) (actual time=0.017..0.017 rows=1 loops=1)
                          Index Cond: (id = property.street_id)
              ->  Index Scan using city_id_pk on city  (cost=0.29..8.30 rows=1 width=25) (actual time=0.010..0.010 rows=1 loops=1)
                    Index Cond: (id = property.city_id)
        ->  Index Scan using state_id_pk on state  (cost=0.28..8.32 rows=1 width=19) (actual time=0.007..0.007 rows=1 loops=1)
              Index Cond: (id = property.state_id)
  ->  Index Scan using zipcode_id_pk on zipcode  (cost=0.29..8.30 rows=1 width=22) (actual time=0.010..0.010 rows=1 loops=1)
        Index Cond: (id = property.zipcode_id)
Planning Time: 2.442 ms
Execution Time: 0.345 ms

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

Почему готовое государство намного медленнее? Какие варианты у меня есть, чтобы сохранить производительность использования оператора, сохраняя возможность использовать заполнитель в запросе?

1 Ответ

2 голосов
/ 07 ноября 2019

Ваш подготовленный оператор преобразует full_address в text (встроенный текстовый тип Postgres), в то время как кажется, что ваша таблица создана с текстовым типом citext (без учета регистра) (или у вас нетиндекс на full_address::text). Возможно, попробуйте создать индекс для full_address::text и посмотреть, подхватит ли его подготовленный оператор.

Другой вариант - использовать тип text для столбца full_address, а затем создать функциональный индекс дляlower(full_address) - приемлемость этого варианта зависит от ваших требований.

Я думаю, что отчасти проблема в том, что JDBC не знает о типе citext, поэтому, если вы не можете заставить JDBCотправьте ваш адрес в базу данных как citext тип, он будет интерпретироваться планировщиком запросов как text, как, вероятно, ваш метод setString().

Интересно, что я выполнилв похожую проблему недавно

Раскрытие информации: я работаю на EnterpriseDB (EDB)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...