From 2f3c178ed0df5fafc2d684b6bd0f7cd5ae1a05f6 Mon Sep 17 00:00:00 2001 From: Struchkov Mark Date: Wed, 19 Jun 2024 19:58:52 +0300 Subject: [PATCH] vault backup: 2024-06-19 19:58:52 --- .obsidian/plugins/home-tab/data.json | 22 +++--- .../plugins/recent-files-obsidian/data.json | 24 +++--- _inbox/Read committed.md | 14 ++++ _inbox/Read uncommitted.md | 9 ++- _inbox/Грязное чтение.md | 75 ++++++++++++++++++- ...ри параллельном выполнении нескольких транзакций.md | 2 - .../database/Уровни изоляций транзакций БД.md | 4 +- 7 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 _inbox/Read committed.md diff --git a/.obsidian/plugins/home-tab/data.json b/.obsidian/plugins/home-tab/data.json index 18431004..ada7b6b7 100644 --- a/.obsidian/plugins/home-tab/data.json +++ b/.obsidian/plugins/home-tab/data.json @@ -24,24 +24,24 @@ "unresolvedLinks": false, "recentFilesStore": [ { - "filepath": "_inbox/Грязное чтение.md", - "timestamp": 1718816024825 - }, - { - "filepath": "_inbox/Read uncommitted.md", - "timestamp": 1718816022761 + "filepath": "knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md", + "timestamp": 1718816327221 }, { "filepath": "knowledge/dev/database/Уровни изоляций транзакций БД.md", - "timestamp": 1718816010418 + "timestamp": 1718816268102 }, { - "filepath": "knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md", - "timestamp": 1718815991051 + "filepath": "_inbox/Грязное чтение.md", + "timestamp": 1718816237993 }, { - "filepath": "_inbox/Транзакция БД.md", - "timestamp": 1718815984227 + "filepath": "_inbox/Read committed.md", + "timestamp": 1718816202958 + }, + { + "filepath": "_inbox/Read uncommitted.md", + "timestamp": 1718816179266 } ], "bookmarkedFileStore": [], diff --git a/.obsidian/plugins/recent-files-obsidian/data.json b/.obsidian/plugins/recent-files-obsidian/data.json index 9795e12a..39b54183 100644 --- a/.obsidian/plugins/recent-files-obsidian/data.json +++ b/.obsidian/plugins/recent-files-obsidian/data.json @@ -1,20 +1,24 @@ { "recentFiles": [ { - "basename": "Грязное чтение", - "path": "_inbox/Грязное чтение.md" - }, - { - "basename": "Read uncommitted", - "path": "_inbox/Read uncommitted.md" + "basename": "Проблемы при параллельном выполнении нескольких транзакций", + "path": "knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md" }, { "basename": "Уровни изоляций транзакций БД", "path": "knowledge/dev/database/Уровни изоляций транзакций БД.md" }, { - "basename": "Проблемы при параллельном выполнении нескольких транзакций", - "path": "knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md" + "basename": "Грязное чтение", + "path": "_inbox/Грязное чтение.md" + }, + { + "basename": "Read committed", + "path": "_inbox/Read committed.md" + }, + { + "basename": "Read uncommitted", + "path": "_inbox/Read uncommitted.md" }, { "basename": "Транзакция БД", @@ -195,10 +199,6 @@ { "basename": "Redis", "path": "_inbox/Redis.md" - }, - { - "basename": "Fingerprint файлов", - "path": "_inbox/Fingerprint файлов.md" } ], "omittedPaths": [], diff --git a/_inbox/Read committed.md b/_inbox/Read committed.md new file mode 100644 index 00000000..2461564d --- /dev/null +++ b/_inbox/Read committed.md @@ -0,0 +1,14 @@ +--- +aliases: +tags: + - зрелость/🌱 +date: + - - 2024-06-19 +zero-link: + - "[[00 Базы Данных]]" +parents: + - "[[Уровни изоляций транзакций БД]]" +linked: +--- +**Read committed (чтение фиксированных данных).** Большинство промышленных СУБД по умолчанию используют именно этот уровень. На этом уровне обеспечивается защита от «грязного» чтения, тем не менее, в процессе работы одной транзакции другая может быть успешно завершена и сделанные ею изменения зафиксированы. В итоге первая транзакция будет работать с другим набором данных. ^11df20 + diff --git a/_inbox/Read uncommitted.md b/_inbox/Read uncommitted.md index ba9fcf02..dd82f25b 100644 --- a/_inbox/Read uncommitted.md +++ b/_inbox/Read uncommitted.md @@ -2,9 +2,12 @@ aliases: tags: - зрелость/🌱 -date: [[2024-06-19]] -zero-link: -parents: +date: + - - 2024-06-19 +zero-link: + - "[[00 Базы Данных]]" +parents: + - "[[Уровни изоляций транзакций БД]]" linked: --- **Read uncommitted (чтение незафиксированных данных).** Если несколько параллельных транзакций пытаются изменять одну и ту же строку таблицы, то в окончательном варианте строка будет иметь значение, определенное всем набором успешно выполненных транзакций. ^52421e \ No newline at end of file diff --git a/_inbox/Грязное чтение.md b/_inbox/Грязное чтение.md index cde1681c..48059614 100644 --- a/_inbox/Грязное чтение.md +++ b/_inbox/Грязное чтение.md @@ -1,5 +1,6 @@ --- -aliases: +aliases: + - dirty reads tags: - зрелость/🌱 date: @@ -8,8 +9,76 @@ zero-link: - "[[00 Базы Данных]]" parents: - "[[Проблемы при параллельном выполнении нескольких транзакций]]" -linked: +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) — в результатах запроса появляются промежуточные результаты параллельной транзакции, которая ещё не завершилась. -Эта проблема наблюдается при уровне изоляции `READ_UNCOMMITTED`. \ No newline at end of file +Эта проблема наблюдается при уровне изоляции [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) \ No newline at end of file diff --git a/knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md b/knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md index 8a515f26..70188d47 100644 --- a/knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md +++ b/knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md @@ -2,8 +2,6 @@ aliases: - phantom reads - фантомное чтение - - dirty reads - - грязное чтение - неповторяющееся чтение - non-repeatable reads - потерянное обновление diff --git a/knowledge/dev/database/Уровни изоляций транзакций БД.md b/knowledge/dev/database/Уровни изоляций транзакций БД.md index 9204dd3a..0b65a411 100644 --- a/knowledge/dev/database/Уровни изоляций транзакций БД.md +++ b/knowledge/dev/database/Уровни изоляций транзакций БД.md @@ -10,9 +10,11 @@ parents: - "[[Транзакция БД]]" linked: --- +У большинства СУБД по умолчанию установлен уровень изоляции [Read committed](Read%20committed.md), а [Read uncommitted](Read%20uncommitted.md) может вовсе не поддерживаться. Более того, некоторые уровни изоляции могут и не иметь описанных далее проблем. Здесь всё индивидуально, изучайте документацию СУБД. + ![](Read%20uncommitted.md#^52421e) -**Read committed (чтение фиксированных данных).** Большинство промышленных СУБД по умолчанию используют именно этот уровень. На этом уровне обеспечивается защита от «грязного» чтения, тем не менее, в процессе работы одной транзакции другая может быть успешно завершена и сделанные ею изменения зафиксированы. В итоге первая транзакция будет работать с другим набором данных. +![](Read%20committed.md#^11df20) **Repeatable read (повторяющееся чтение).** Уровень, при котором читающая транзакция «не видит» изменения данных, которые были ею ранее прочитаны. При этом никакая другая транзакция не может изменять данные, читаемые текущей транзакцией, пока та не окончена.