--- aliases: - dirty reads tags: - зрелость/🌱 date: - - 2024-06-19 zero-link: - "[[00 Базы Данных]]" parents: - "[[Проблемы при параллельном выполнении нескольких транзакций]]" linked: link: https://struchkov.dev/blog/ru/transactional-isolation-levels/#%D0%B3%D1%80%D1%8F%D0%B7%D0%BD%D0%BE%D0%B5-%D1%87%D1%82%D0%B5%D0%BD%D0%B8%D0%B5 --- **«Грязное» чтение** (dirty reads) — в результатах запроса появляются промежуточные результаты параллельной транзакции, которая ещё не завершилась. ^c744ef Эта проблема наблюдается при уровне изоляции [Read uncommitted](Read%20uncommitted.md). Рассмотрим на примере. У нас будет два параллельных потока. В первом мы открываем транзакцию и устанавливаем новый баланс первому пользователю равным 100_000. Но транзакцию не коммитим. Вместо этого, запускаем вторую параллельную транзакцию в отдельном потоке, а текущий поток засыпает на 2 секунды. Во второй транзакции считывается баланс пользователя из БД и выводится в консоль. Значение баланса будет 100_000, несмотря на то, что первая транзакция ещё не закоммитила свои изменения. Но дальше самое интересное, мы выполняем rollback первой транзакции, и баланс пользователя снова становится 1000. То есть вторая транзакция работала с не валидными данными. ```java public class DirtyReadExample { private static final int ISOLATION_LEVEL = Connection.TRANSACTION_READ_UNCOMMITTED; public static void main(String[] args) throws SQLException, InterruptedException { try ( final Connection connection = Repository.getConnectionH2(); final Statement statement = connection.createStatement() ) { connection.setAutoCommit(false); connection.setTransactionIsolation(ISOLATION_LEVEL); statement.executeUpdate("UPDATE person SET balance = 100000 WHERE id = 1"); new OtherTransaction().start(); Thread.sleep(2000); connection.rollback(); } } static class OtherTransaction extends Thread { @Override public void run() { try ( final Connection connection = Repository.getConnectionH2(); final Statement statement = connection.createStatement() ) { connection.setAutoCommit(false); connection.setTransactionIsolation(ISOLATION_LEVEL); final ResultSet resultSet = statement.executeQuery("SELECT * FROM person WHERE id = 1"); while (resultSet.next()) { System.out.println("Balance: " + resultSet.getString("balance")); } } catch (SQLException e) { System.out.println(e.getMessage()); } } } } ``` Результат работы примера: ```log Balance: 100000 ``` Для устранения этого эффекта устанавливаем уровень изоляции в [Read committed](Read%20committed.md). Теперь при выполнении второй транзакции получаем баланс 1000, вместо 100_000. Результат работы примера с `READ_COMMITTED`: ```log Balance: 1000 ``` ## Дополнительные материалы - [Пример реализации на Java](https://github.com/Example-uPagge/transactional/blob/master/jdbc-transaction/src/main/java/dev/struchkov/example/transaction/problems/DirtyReadExample.java)