PreparedStatements и производительность - PullRequest
51 голосов
/ 27 марта 2009

Так что я продолжаю слышать, что PreparedStatements хороши для производительности.

У нас есть Java-приложение, в котором мы используем регулярное «Заявление» больше, чем «PreparedStatement». Пытаясь перейти к использованию большего количества PreparedStatements, я пытаюсь получить более глубокое понимание того, как работают PreparedStatements - на стороне клиента и на стороне сервера.

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

Так как это помогает с производительностью? Кэширует ли драйвер предварительно скомпилированный оператор и дает ли мне копию в следующий раз, когда я выполняю connection.prepareStatement? Или сервер БД помогает?

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

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

    // some code blah blah
    update();

    // some more code blah blah 
    update();

.... 

public void update () throws SQLException{
 try{
      PreparedStatement ps = connection.prepareStatement("some sql");
      ps.setString(1, "foobar1");
      ps.setString(2, "foobar2");
      ps.execute();
 }finally {
     ps.close();

 }

}

Нет никакого способа повторно использовать java-объект 'ps', и я понимаю, что фактический вызов connection.prepareStatement довольно дорогой.

Именно это возвращает меня к первоначальному вопросу. Этот PreparedStatement "некоторого sql" все еще кэшируется и повторно используется под обложками, о которых я не знаю?

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

Заранее спасибо.

Ответы [ 10 ]

29 голосов
/ 27 марта 2009

Представление о том, что подготовленные заявления в основном касаются производительности, является ошибочным, хотя и довольно распространенным.

Другой автор упомянул, что он отметил повышение скорости примерно на 20% в Oracle и SQL Server. Я заметил аналогичную цифру с MySQL. Оказывается, что синтаксический анализ запроса не является такой важной частью работы. В очень загруженной системе баз данных также неясно, что синтаксический анализ запросов повлияет на общую пропускную способность: в целом, возможно, он просто использует процессорное время, которое в противном случае было бы бездействующим, когда данные возвращались с диска.

Таким образом, в качестве причины использования подготовленных операторов защита от атак с использованием SQL-инъекций намного превышает улучшение производительности. И если вас не беспокоят атаки с использованием SQL-инъекций, вам, вероятно, следует ...

27 голосов
/ 27 марта 2009

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

PreparedStatement ps = connection.prepare("SOME SQL");

for (Data data : dataList) {
  ps.setInt(1, data.getId());
  ps.setString(2, data.getValue();
  ps.executeUpdate();
}

ps.close();

Это намного быстрее, чем создание оператора в цикле.

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

Однако даже если производительность была одинаковой, вы все равно должны использовать подготовленные операторы для предотвращения SQL-инъекций. В моей компании это вопрос интервью; поймите неправильно, и мы можем вас не нанять.

13 голосов
/ 27 марта 2009

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

Более подробную информацию можно найти здесь:

http://www.theserverside.com/tt/articles/article.tss?l=Prepared-Statments

и вы можете рассмотреть Spring JDBCTemplate как альтернативу непосредственному использованию JDBC.

http://static.springframework.org/spring/docs/2.0.x/reference/jdbc.html

8 голосов
/ 27 марта 2009

Разбор SQL - не единственное, что происходит. Существует проверка того, что таблицы и столбцы действительно существуют, создание плана запроса и т. Д. Вы платите один раз с помощью PreparedStatement.

Привязка для защиты от SQL-инъекций действительно очень хорошая вещь. Не достаточно, ИМО. Вам все еще следует проверить ввод перед тем, как перейти к постоянному слою.

4 голосов
/ 11 декабря 2014

Так как это помогает с производительностью? Кеширует ли драйвер предварительно скомпилированное заявление и дайте мне копию в следующий раз, когда я сделаю connection.prepareStatement? Или сервер БД помогает?

Я отвечу с точки зрения производительности. Другие здесь уже указали, что PreparedStatement s устойчивы к SQL-инъекциям (благословенное преимущество).

Приложение (драйвер JDBC) создает PreparedStatement и передает его в СУБД с заполнителями (?). СУБД прекомпилируется, применяя оптимизацию запросов (при необходимости) полученных PreparedStatement и (в некоторых) обычно их кэширует. Во время выполнения PreparedStatement используется предварительно скомпилированный PreparedStatement, заменяя каждый заполнитель соответствующими значениями и вычисляя их. В отличие от Statement, который компилирует и выполняет его напрямую, PreparedStatement компилирует и оптимизирует запрос только один раз . Теперь этот сценарий, описанный выше, не является абсолютным случаем ВСЕХ поставщиков JDBC, но по сути это то, как PreparedStatement используется и работает.

3 голосов
/ 08 декабря 2014

Именно это возвращает меня к первоначальному вопросу. Является ли этот PreparedStatement «некоторый sql» все еще кэшируемым и повторно используемым под обложками, о которых я не знаю?

Да, по крайней мере, с Oracle. Согласно Oracle® Database JDBC. Руководство разработчика Неявное кэширование операторов (выделение добавлено),

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

Простые операторы не кэшируются неявно, потому что неявное кэширование операторов использует строку SQL в качестве ключа, а простые операторы создаются без строки SQL. Следовательно, неявное кэширование операторов применяется только к объектам OraclePreparedStatement и OracleCallableStatement, которые создаются с помощью строки SQL. Вы не можете использовать неявное кэширование операторов с OracleStatement. При создании OraclePreparedStatement или OracleCallableStatement драйвер JDBC автоматически ищет в кэше соответствующий оператор .

3 голосов
/ 27 марта 2009

Анекдотично: я провел несколько экспериментов с подготовленными и динамическими операторами с использованием ODBC в Java 1.4 несколько лет назад, с использованием как Oracle, так и SQL Server. Я обнаружил, что подготовленные операторы могут быть на 20% быстрее для определенных запросов, но были различия между поставщиками в отношении того, какие запросы были улучшены до какой степени. (Это не должно удивлять, правда.)

Суть в том, что если вы будете повторно использовать один и тот же запрос несколько раз, подготовленные операторы могут помочь повысить производительность; но если ваша производительность достаточно плохая, и вам нужно немедленно что-то с этим делать, не рассчитывайте на использование подготовленных высказываний для радикального повышения. (20% обычно не о чем писать.)

Ваш пробег, конечно, может отличаться.

2 голосов
/ 12 декабря 2014

1. PreparedStatement позволяет писать динамический и параметрический запрос

Используя PreparedStatement в Java, вы можете писать параметризованные SQL-запросы и отправлять различные параметры, используя одни и те же SQL-запросы, что намного лучше, чем создавать различные запросы.

2. PreparedStatement работает быстрее, чем Statement в Java

Одним из основных преимуществ использования PreparedStatement является повышение производительности. PreparedStatement получает предварительно скомпилированный В базе данных и там план доступа также кэшируется в базе данных, что позволяет базе данных выполнять параметрический запрос, написанный с использованием подготовленного оператора, намного быстрее, чем обычный запрос, потому что у него меньше работы. Вы всегда должны пытаться использовать PreparedStatement в рабочем коде JDBC, чтобы уменьшить нагрузку на базу данных. Чтобы получить выигрыш в производительности, стоит отметить использование только параметризованной версии SQL-запроса, а не с конкатенацией строк

3. PreparedStatement предотвращает атаки SQL-инъекций в Java

Подробнее: http://javarevisited.blogspot.com/2012/03/why-use-preparedstatement-in-java-jdbc.html#ixzz3LejuMnVL

1 голос
/ 20 января 2016

Краткий ответ:

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

Длинный ответ:

Согласно Википедии , типичный рабочий процесс использования подготовленного оператора выглядит следующим образом:

Подготовка : шаблон выписки создается приложением и отправляется в систему управления базами данных (СУБД). Определенные значения остались неопределенные, вызываемые параметры, заполнители или переменные связывания (с пометкой «?» ниже): ВСТАВИТЬ В ПРОДУКТ (название, цена) ЗНАЧЕНИЯ (?,?)

(прекомпиляция) : СУБД анализирует, компилирует и выполняет оптимизацию запросов на шаблон выписки и сохраняет результат без его выполнения.

Выполнить : позднее приложение предоставляет (или связывает) значения для параметров, и СУБД выполняет инструкцию (возможно, возвращая результат). Приложение может выполнить инструкцию как можно больше раз, как он хочет с разными значениями. В этом примере это может поставьте «Хлеб» для первого параметра и «1,00» для второго параметр.

Приготовьте:

В JDBC шаг «Подготовка» выполняется путем вызова java.sql.Connection. prepareStatement (String sql) API. Согласно его Javadoc:

Этот метод оптимизирован для обработки параметрических операторов SQL, которые выигрывают от предварительной компиляции. Если драйвер поддерживает прекомпиляцию, метод prepareStatement отправит инструкцию в базу данных для прекомпиляции. Некоторые драйверы могут не поддерживать прекомпиляцию. В этом случае инструкция не может быть отправлена ​​в базу данных, пока не будет выполнен объект PreparedStatement. Это не имеет прямого влияния на пользователей; однако это влияет на то, какие методы генерируют определенные объекты SQLException.

Поскольку вызов этого API может отправлять оператор SQL в базу данных, обычно это дорогой вызов. В зависимости от реализации драйвера JDBC, если у вас один и тот же шаблон оператора SQL, для повышения производительности вам, возможно, придется избегать многократного вызова этого API на стороне клиента для одного и того же шаблона оператора SQL.

Прекомпиляция:

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

Execute:

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

Вывод:

С точки зрения производительности, использование оператора подготовки - двухэтапный процесс:

  1. Этап 1, подготовка и предварительная компиляция, ожидается, что этот этап будет сделать один раз и добавить некоторые накладные расходы на производительность.
  2. Фаза 2, повторное выполнение одного и того же запроса, так как фаза 1 имеет некоторые предварительные обработка запроса, если количество повторяющихся запросов велико достаточно, это может сэкономить много предварительной обработки для того же запрос.

И если вы хотите узнать больше деталей, есть несколько статей, объясняющих преимущества PrepareStatement:

  1. http://javarevisited.blogspot.com/2012/03/why-use-preparedstatement-in-java-jdbc.html
  2. http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html
1 голос
/ 11 декабря 2014

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

Например, с точки зрения производительности базы данных, база данных Oracle кэширует план выполнения некоторых запросов после каждого вычисления (это не верно для всех версий и всех конфигураций Oracle). Вы можете найти улучшения, даже если вы закроете оператор и откроете новый, потому что это делается на уровне РСУБД. Этот вид кэширования активируется, только если два последующих запроса (char-by-char) одинаковы. Это не относится к обычным операторам, поскольку параметры являются частью запроса и создают разные строки SQL.

Некоторые другие СУБД могут быть более «интеллектуальными», но я не ожидаю, что они будут использовать сложные алгоритмы сопоставления с образцом для кэширования планов выполнения, поскольку это снизит производительность. Вы можете утверждать, что вычисление плана выполнения является лишь малой частью выполнения запроса. Для общего случая я согласен, но .. это зависит. Имейте в виду, что обычно вычисление плана выполнения может быть дорогостоящей задачей, поскольку rdbms должен обращаться к данным вне памяти, таким как статистика (не только Oracle).

Однако аргумент о кешировании варьируется от планов выполнения до других частей процесса извлечения. Многократное предоставление RDBMS одного и того же запроса (без углубленного изучения конкретной реализации) помогает идентифицировать уже вычисленные структуры на уровне JDBC (драйвер) или RDBMS. Если вы не обнаружите каких-либо особых преимуществ в производительности сейчас, вы не можете исключить, что повышение производительности будет реализовано в будущих / альтернативных версиях драйвера / rdbms.

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

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