--- aliases: - phantom reads tags: - зрелость/🌱 date: - - 2024-06-19 zero-link: - "[[00 Базы Данных]]" parents: - "[[Проблемы при параллельном выполнении нескольких транзакций]]" linked: link: https://struchkov.dev/blog/ru/transactional-isolation-levels/#%D1%84%D0%B0%D0%BD%D1%82%D0%BE%D0%BC%D0%BD%D0%BE%D0%B5-%D1%87%D1%82%D0%B5%D0%BD%D0%B8%D0%B5 --- **Фантомное чтение (phantom reads).** В результатах повторяющегося запроса появляются и исчезают строки, которые в данный момент модифицирует параллельная транзакция. ^ebb2ec Эта проблема присутствует на уровне изоляции [Repeatable read](Repeatable%20read.md), [Read committed](Read%20committed.md) и [Read uncommitted](Read%20uncommitted.md). Рассмотрим пример. Открываем первую транзакцию. Запрашиваем количество строк в таблице пользователей, получаем ответ 2. Далее стартуем вторую транзакцию, а поток с первой транзакцией засыпает. Вторая транзакция добавляет новую запись в таблицу пользователей и коммитит изменения. После этого первая транзакция продолжается. Снова запрашиваем количество строк, получаем на этот раз ответ 3. ```java public class PhantomRead { private static final int ISOLATION_LEVEL = Connection.TRANSACTION_READ_COMMITTED; public static void main(String[] args) { try( final Connection connection = Repository.getConnection(); final Statement statement = connection.createStatement() ) { connection.setAutoCommit(false); connection.setTransactionIsolation(ISOLATION_LEVEL); final ResultSet resultSet = statement.executeQuery("SELECT count(*) FROM person"); while (resultSet.next()) { final int count = resultSet.getInt(1); System.out.println("Count: " + count); } new OtherTransaction().start(); Thread.sleep(2000); final ResultSet resultSetTwo = statement.executeQuery("SELECT count(*) FROM person"); while (resultSetTwo.next()) { final int count = resultSetTwo.getInt(1); System.out.println("Count: " + count); } } catch (SQLException | InterruptedException e) { throw new RuntimeException(e); } } static class OtherTransaction extends Thread { @Override public void run() { try ( final Connection connection = Repository.getConnection(); final Statement statement = connection.createStatement() ) { connection.setAutoCommit(false); connection.setTransactionIsolation(ISOLATION_LEVEL); statement.executeUpdate("INSERT INTO person(id, balance) values (3, 1000)"); connection.commit(); } catch (SQLException e) { System.out.println(e.getMessage()); } } } } ``` Чтобы устранить эту проблему, используем самый изолированный уровень [Serializable](Serializable.md). Однако, для PostgreSQL будет достаточно и `REPEATABLE_READ`. Но мы всё равно установим `SERIALIZABLE`. Теперь в первой транзакции нам дважды возвращается ответ 2, несмотря на то, что в таблице появились новые записи. ## Дополнительные материалы - [Пример на Java](https://github.com/Example-uPagge/transactional/blob/master/jdbc-transaction/src/main/java/dev/struchkov/example/transaction/problems/PhantomRead.java)