vault backup: 2024-06-19 20:08:52
This commit is contained in:
parent
32137b40ef
commit
c4c3431957
20
.obsidian/plugins/home-tab/data.json
vendored
20
.obsidian/plugins/home-tab/data.json
vendored
@ -24,24 +24,24 @@
|
|||||||
"unresolvedLinks": false,
|
"unresolvedLinks": false,
|
||||||
"recentFilesStore": [
|
"recentFilesStore": [
|
||||||
{
|
{
|
||||||
"filepath": "_inbox/Безмастерная репликация.md",
|
"filepath": "knowledge/dev/database/Уровни изоляций транзакций БД.md",
|
||||||
"timestamp": 1718816632068
|
"timestamp": 1718816928649
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filepath": "_inbox/Транзакция БД.md",
|
||||||
|
"timestamp": 1718816827388
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filepath": "knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md",
|
"filepath": "knowledge/dev/database/Проблемы при параллельном выполнении нескольких транзакций.md",
|
||||||
"timestamp": 1718816630837
|
"timestamp": 1718816823586
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filepath": "_inbox/Потерянное обновление.md",
|
"filepath": "_inbox/Потерянное обновление.md",
|
||||||
"timestamp": 1718816620297
|
"timestamp": 1718816800840
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filepath": "_inbox/Неповторяющееся чтение.md",
|
"filepath": "_inbox/Race condition.md",
|
||||||
"timestamp": 1718816570534
|
"timestamp": 1718816771915
|
||||||
},
|
|
||||||
{
|
|
||||||
"filepath": "_inbox/Фантомное чтение.md",
|
|
||||||
"timestamp": 1718816475988
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bookmarkedFileStore": [],
|
"bookmarkedFileStore": [],
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
{
|
{
|
||||||
"recentFiles": [
|
"recentFiles": [
|
||||||
{
|
{
|
||||||
"basename": "Безмастерная репликация",
|
"basename": "Уровни изоляций транзакций БД",
|
||||||
"path": "_inbox/Безмастерная репликация.md"
|
"path": "knowledge/dev/database/Уровни изоляций транзакций БД.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "Транзакция БД",
|
||||||
|
"path": "_inbox/Транзакция БД.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "Проблемы при параллельном выполнении нескольких транзакций",
|
"basename": "Проблемы при параллельном выполнении нескольких транзакций",
|
||||||
@ -12,6 +16,14 @@
|
|||||||
"basename": "Потерянное обновление",
|
"basename": "Потерянное обновление",
|
||||||
"path": "_inbox/Потерянное обновление.md"
|
"path": "_inbox/Потерянное обновление.md"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"basename": "Race condition",
|
||||||
|
"path": "_inbox/Race condition.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "Безмастерная репликация",
|
||||||
|
"path": "_inbox/Безмастерная репликация.md"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"basename": "Неповторяющееся чтение",
|
"basename": "Неповторяющееся чтение",
|
||||||
"path": "_inbox/Неповторяющееся чтение.md"
|
"path": "_inbox/Неповторяющееся чтение.md"
|
||||||
@ -24,10 +36,6 @@
|
|||||||
"basename": "Полу-синхронная репликация",
|
"basename": "Полу-синхронная репликация",
|
||||||
"path": "_inbox/Полу-синхронная репликация.md"
|
"path": "_inbox/Полу-синхронная репликация.md"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"basename": "Уровни изоляций транзакций БД",
|
|
||||||
"path": "knowledge/dev/database/Уровни изоляций транзакций БД.md"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"basename": "Грязное чтение",
|
"basename": "Грязное чтение",
|
||||||
"path": "_inbox/Грязное чтение.md"
|
"path": "_inbox/Грязное чтение.md"
|
||||||
@ -40,10 +48,6 @@
|
|||||||
"basename": "Read uncommitted",
|
"basename": "Read uncommitted",
|
||||||
"path": "_inbox/Read uncommitted.md"
|
"path": "_inbox/Read uncommitted.md"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"basename": "Транзакция БД",
|
|
||||||
"path": "_inbox/Транзакция БД.md"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"basename": "Memcached",
|
"basename": "Memcached",
|
||||||
"path": "_inbox/Memcached.md"
|
"path": "_inbox/Memcached.md"
|
||||||
@ -68,10 +72,6 @@
|
|||||||
"basename": "Reliability",
|
"basename": "Reliability",
|
||||||
"path": "_inbox/Reliability.md"
|
"path": "_inbox/Reliability.md"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"basename": "Race condition",
|
|
||||||
"path": "_inbox/Race condition.md"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"basename": "Home",
|
"basename": "Home",
|
||||||
"path": "Home.md"
|
"path": "Home.md"
|
||||||
|
@ -10,3 +10,4 @@ zero-link:
|
|||||||
parents:
|
parents:
|
||||||
linked:
|
linked:
|
||||||
---
|
---
|
||||||
|
Пример race condition в БД: ![](Pasted%20image%2020240619200549.png)
|
@ -40,5 +40,5 @@ linked:
|
|||||||
- [Нестрогий кворум](Нестрогий%20кворум.md). Возможно чтение старых данных при W+R < N
|
- [Нестрогий кворум](Нестрогий%20кворум.md). Возможно чтение старых данных при W+R < N
|
||||||
- Нет отката транзакций.
|
- Нет отката транзакций.
|
||||||
- Как в таком случае работает обновление при чтении или противодействие энтропии, ведь эти данные становятся новыми.
|
- Как в таком случае работает обновление при чтении или противодействие энтропии, ведь эти данные становятся новыми.
|
||||||
- Конфликт записей и [потерянное обновление](Проблемы%20при%20параллельном%20выполнении%20нескольких%20транзакций.md)
|
- Конфликт записей и [Потерянное обновление](Потерянное%20обновление.md).
|
||||||
- Проблемы с линеаризуемостью.
|
- Проблемы с линеаризуемостью.
|
@ -12,3 +12,80 @@ parents:
|
|||||||
linked:
|
linked:
|
||||||
---
|
---
|
||||||
**Потерянное обновление (lost update).** Две параллельные транзакции меняют одни и те же данные, при этом итоговый результат обновления предсказать невозможно. ^23d01d
|
**Потерянное обновление (lost update).** Две параллельные транзакции меняют одни и те же данные, при этом итоговый результат обновления предсказать невозможно. ^23d01d
|
||||||
|
|
||||||
|
Рассмотрим эту проблему на примере:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class LostUpdateExample {
|
||||||
|
|
||||||
|
public static final String READ = "SELECT person.balance FROM person WHERE id = ?";
|
||||||
|
public static final String UPDATE = "UPDATE person SET balance = ? WHERE id = ?";
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
// Начинаем две транзакции.
|
||||||
|
final Connection connectionOne = getNewConnection();
|
||||||
|
final Connection connectionTwo = getNewConnection();
|
||||||
|
|
||||||
|
// Первая и вторая транзакция запрашивают баланс пользователя.
|
||||||
|
// balance = 1000
|
||||||
|
final long balanceOne = getBalance(connectionOne);
|
||||||
|
final long balanceTwo = getBalance(connectionTwo);
|
||||||
|
|
||||||
|
// Первая транзакция готовится обновить баланс пользователю.
|
||||||
|
final PreparedStatement updateOne = connectionOne.prepareStatement(UPDATE);
|
||||||
|
updateOne.setLong(1, balanceOne + 10);
|
||||||
|
updateOne.setLong(2, 1);
|
||||||
|
updateOne.execute();
|
||||||
|
|
||||||
|
// Первая транзакция фиксирует изменения и завершается.
|
||||||
|
// Значение balance в базе в этот момент = 1010.
|
||||||
|
connectionOne.commit();
|
||||||
|
connectionOne.close();
|
||||||
|
|
||||||
|
// Но вторая транзакция ничего не знает про изменения в БД.
|
||||||
|
// Значение balanceTwo все еще равно 1000, к этому значению мы добавляем 5.
|
||||||
|
final PreparedStatement updateTwo = connectionTwo.prepareStatement(UPDATE);
|
||||||
|
updateTwo.setLong(1, balanceTwo + 5);
|
||||||
|
updateTwo.setLong(2, 1);
|
||||||
|
updateTwo.execute();
|
||||||
|
|
||||||
|
// Вторая транзакция фиксирует свои изменения и завершается.
|
||||||
|
// В итоге в БД остается значение 1005, а не 1015, как хотелось бы нам.
|
||||||
|
connectionTwo.commit();
|
||||||
|
connectionTwo.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Connection getNewConnection() throws SQLException {
|
||||||
|
final Connection connection = Repository.getConnection();
|
||||||
|
connection.setAutoCommit(false);
|
||||||
|
connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getBalance(Connection connectionOne) throws SQLException {
|
||||||
|
final PreparedStatement preparedStatement = connectionOne.prepareStatement(READ);
|
||||||
|
preparedStatement.setLong(1, 1);
|
||||||
|
final ResultSet resultSet = preparedStatement.executeQuery();
|
||||||
|
resultSet.next();
|
||||||
|
final long balanceOne = resultSet.getLong(1);
|
||||||
|
return balanceOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
На строках 10, 11 мы получаем 2 независимых соединения с БД, в которых отключён auto-commit и установлен уровень изоляции [Read committed](Read%20committed.md).
|
||||||
|
|
||||||
|
На строках 15 и 16 обе транзакции получают баланс первого пользователя. Напомню, что баланс равен 1000.
|
||||||
|
|
||||||
|
На строках 19-22 выполняется обновление баланса пользователя первой транзакцией. Для этого к полученному ранее балансу прибавляется 10. Но сейчас эти изменения не были отправлены в БД. Это произойдёт только при закрытии транзакции.
|
||||||
|
|
||||||
|
Закрываем первую транзакцию (26, 27). Баланс пользователя в БД равен 1010. Но вторая транзакция идёт параллельно, и ничего не знает об изменении баланса пользователя. Ведь баланс мы уже считали из БД, и там было 1000.
|
||||||
|
|
||||||
|
Вторая транзакция прибавляет к балансу пользователя 5 (31-34). После чего вторая транзакция также закрывается (38, 39). Баланс пользователя в БД равен 1005. Мы потеряли обновления, которые выполнила первая транзакция. Такое поведение называют [Race condition](Race%20condition.md).
|
||||||
|
|
||||||
|
Изменим уровень транзакций на более изолированный. В примере у нас используется `READ_COMMITTED`, установим в строке 45.
|
||||||
|
|
||||||
|
В таком случае при выполнении нашего кода получаем исключение `PSQLException`.
|
@ -15,12 +15,18 @@ linked:
|
|||||||
|
|
||||||
Для реализации обычно используется [Журнал БД](Журнал%20БД.md).
|
Для реализации обычно используется [Журнал БД](Журнал%20БД.md).
|
||||||
|
|
||||||
- [Свойства транзакции БД (ACID)](Свойства%20транзакции%20БД%20(ACID).md)
|
|
||||||
- [Проблемы при параллельном выполнении нескольких транзакций](Проблемы%20при%20параллельном%20выполнении%20нескольких%20транзакций.md)
|
|
||||||
- [Уровни изоляций транзакций БД](Уровни%20изоляций%20транзакций%20БД.md)
|
|
||||||
|
|
||||||
**Как происходит commit транзакции:**
|
**Как происходит commit транзакции:**
|
||||||
- Подготовка транзакции в движке БД
|
- Подготовка транзакции в движке БД
|
||||||
- Запись транзакции в лог
|
- Запись транзакции в лог
|
||||||
- Завершение транзакции в движке БД
|
- Завершение транзакции в движке БД
|
||||||
- Возврат результата клиенту
|
- Возврат результата клиенту
|
||||||
|
|
||||||
|
**Свойства транзакций БД:**
|
||||||
|
![Свойства транзакции БД (ACID)](Свойства%20транзакции%20БД%20(ACID).md)
|
||||||
|
|
||||||
|
**Проблемы при параллельном выполнении нескольких транзакций:**
|
||||||
|
![Проблемы при параллельном выполнении нескольких транзакций](Проблемы%20при%20параллельном%20выполнении%20нескольких%20транзакций.md)
|
||||||
|
|
||||||
|
**Уровни изоляций транзакций БД:**
|
||||||
|
![Уровни изоляций транзакций БД](Уровни%20изоляций%20транзакций%20БД.md)
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
---
|
---
|
||||||
aliases:
|
aliases: []
|
||||||
- потерянное обновление
|
|
||||||
- lost update
|
|
||||||
tags:
|
tags:
|
||||||
- зрелость/🌱
|
- зрелость/🌱
|
||||||
date:
|
date:
|
||||||
|
BIN
meta/files/Pasted image 20240619200549.png
Normal file
BIN
meta/files/Pasted image 20240619200549.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 321 KiB |
Loading…
Reference in New Issue
Block a user