Статьи

Азбука JDBC, часть 5 — Сделки

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

Прочитайте другие части в серии часто задаваемых вопросов JDBC DZone :

 

Какие аномалии могут возникнуть без правильной транзакции и блокировки?

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

Эти   аномалии перечислены ниже:


  1. Грязное чтение : Технически говоря, грязное чтение происходит, когда транзакция считывает некоторые данные, которые изменяются другой транзакцией, которая еще не зафиксирована.

  2. Неповторяемые операции чтения :   неповторяемые операции чтения происходят, когда транзакция считывает некоторые записи, которые модифицирует другая транзакция. Если транзакция чтения повторяет тот же запрос в той же транзакции, результат будет другим по сравнению с первым разом.

  3. Фантомное чтение : происходит, когда мы читаем набор записей с определенным предложением WHERE, а другая операция вставляет новые записи, соответствующие нашему предложению WHERE. Наша транзакция прочитала записи без учета вставляемой записи.

 

Что такое блокировка базы данных?

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

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

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

 

Что такое оптимистичный параллелизм?

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

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

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

 

Что такое пессимистический параллелизм?

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

 

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

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

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

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

 

Как транзакции управляются в JDBC?

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

В следующем примере кода показано, как вручную запустить и зафиксировать транзакцию:

con.setAutoCommit(false);
Statement stmt = con.createStatement();
stmt.executeUpdate("insert into student(field1) values (110)");
// doing some other operations like sending email, performing other db operations //and finally committing the transaction.
con.commit();//committing the transaction and persisting the insert operation.

 

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

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

 

Каковы стандартные уровни изоляции, определенные JDBC?

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


  • TRANSACTION_NONE : константа, указывающая, что транзакции не поддерживаются.

  • TRANSACTION_READ_COMMITTED :   константа, указывающая, что грязное чтение предотвращено; могут произойти неповторяющиеся чтения и фантомные чтения.
  • TRANSACTION_READ_UNCOMMITTED :   константа, указывающая, что могут происходить грязные чтения, неповторяющиеся чтения и фантомные чтения.
  • TRANSACTION_REPEATABLE_READ : константа, указывающая, что грязные чтения и неповторяющиеся чтения предотвращены; может произойти фантомное чтение.

  • TRANSACTION_SERIALIZABLE : константа, указывающая, что грязные чтения, неповторяющиеся чтения и фантомные чтения предотвращены.

Все эти константы имеют тип int и существуют в интерфейсе JDBC Connection.

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

Connection con = DriverManager.getConnection(url);
DatabaseMetaData dbmd = con.getMetaData();
if (dbmd.supportsTransactionIsolationLevel(TRANSACTION_SERIALIZABLE)
{ con.setTransactionIsolation(TRANSACTION_SERIALIZABLE); }
con.setAutoCommit(false);
// doing the transactional tasks
con.commit();

 

В следующей таблице показано, какой уровень изоляции выдерживает какое-либо из явлений.

 

Что такое точки сохранения?

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

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

Следующий пример кода демонстрирует использование точек сохранения:

con.setAutoCommit(false);
Statement stmt = con.createStatement();
stmt.executeUpdate("insert into student(field1) values (110)");
//set savepoint
Savepoint svpt1 = conn.setSavepoint("SAVEPOINT_1");
rows = stmt.executeUpdate("insert into student(field2) values ('value')");
conn.rollback(svpt1);
//rolling back the transaction up to the insert operation
con.commit();
//committing the transaction and persisting the insert operation.

 

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

 

Каковы соображения для определения границ транзакции?

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


  • По возможности используйте ручную обработку транзакций.

  • Пакетные операции, насколько позволяет бизнес-сфера, вместе повышают общую производительность.

  • Используйте уровни изоляции осторожно; более ограничительный уровень изоляции означает, что больше транзакций будет заблокировано для выполняемой транзакции. Решите, какой уровень изоляции действительно требуется, консультируясь с документами бизнес-аналитика.

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

  • Точная настройка атрибутов повышения блокировки базы данных в соответствии с характеристиками системы.