Files
db/labs/lab5/report.typ
2025-12-15 22:27:56 +03:00

1223 lines
37 KiB
Typst
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#show link: underline
#set page(
header: context {
if counter(page).get().first() == 1 [
#align(center)[
Санкт-Петербургский национальный исследовательский университет информационных технологий, механики и оптики
]
]
},
footer: context {
if counter(page).get().first() == 1 [
#align(center)[Санкт-Петербург \ 2025]
] else [
#align(center)[#counter(page).display("1")]
]
}
)
#show raw.where(block: false): box.with(
fill: luma(240),
inset: (x: 3pt, y: 0pt),
outset: (y: 3pt),
radius: 2pt,
)
#show raw.where(block: true): block.with(
fill: luma(240),
inset: 10pt,
radius: 4pt,
)
// title
\
\
\
#align(center)[Факультет инфокоммуникационных технологий]
#align(center)[Направление подготовки 11.03.02]
\
\
#align(center)[Практическая работа №5]
//#align(center)[Установка и первоначальная настройка субд postgresql.]
\
\
\
\ //#align(center)[Вариант 19]
\
\
\
\
\
\
\
#align(right)[Выполнил:]
#align(right)[Дощенников Никита Андреевич]
#align(right)[Группа: К3221]
#align(right)[Проверила:]
#align(right)[Татьяна Евгеньевна Войтюк]
#pagebreak()
=== Цель работы
Изучить механику транзакций в postgresql, понять принципы работы блокировок на уровне строк, разобраться с поведением конкурентных подключений, а также отработать применение операторов `BEGIN`, `COMMIT`, `ROLLBACK`, сохранённых точек и диагностику ошибок, возникающих при конфликте транзакций.
=== Задачи, решаемые при выполнении работы
- Выполнить создание и модификацию данных в таблицах с использованием транзакций.
- Исследовать работу блокировок при одновременном доступе из нескольких подключений.
- Проверить поведение транзакции при возникновении ошибок.
- Освоить применение `SAVEPOINT`, `ROLLBACK TO SAVEPOINT`, `COMMIT` и полного `ROLLBACK`.
- Проанализировать различия в изоляции и видимости изменений между параллельными сессиями.
- Сделать выводы о корректном использовании транзакций для обеспечения целостности данных.
=== Исходные данные
1. База данных postgresql, содержащая таблицы схемы "EmployeesDepartments" и таблицу `public.t1`.
2. Доступ к системе с двух независимых подключений для моделирования конкурентной работы.
3. Набор sql-операторов для выполнения транзакций: `BEGIN`, `UPDATE`, `SELECT`, `COMMIT`, `ROLLBACK`, `SAVEPOINT`.
4. Записи, над которыми проводились операции.
5. Средства для выполнения запросов и наблюдения за блокировками и поведением транзакций.
=== Выполнение работы
// Краткое описание процесса выполнения всех задач по шагам (при наличии нескольких шагов) со скриншотами.
==== Задание 1. Изучение транзакций COMMIT и ROLLBACK в PostgreSQL
Сначала я выключил автокоммит в настройках pgadmin. (@img1)
#align(center)[
#figure(
image("assets/1.png"),
supplement: [Рис.],
caption: [Выключение автокоммитов.]
) <img1>
]
Я открыл "Querry Tool" и ввел в окне запроса код с @script1: (@img2)
#align(center)[
#figure(
```sql
SELECT 'Before' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 101;
```,
supplement: [Скрипт.],
caption: [Выборка данных сотрудника до начала транзакции.]
) <script1>
]
Запрашивает текущее значение полей сотрудника с ID 101, чтобы зафиксировать исходное состояние перед выполнением изменений в транзакции.
#align(center)[
#figure(
image("assets/2.png"),
supplement: [Рис.],
caption: [Результат выполнения @script1]
) <img2>
]
#align(center)[
#figure(
```sql
BEGIN;
```,
supplement: [Скрипт.],
caption: [Начало транзакции.]
) <script2>
]
Запускает новую транзакцию, в рамках которой дальнейшие операции будут выполняться атомарно и не будут видны другим сессиям до `COMMIT` или `ROLLBACK`.
#align(center)[
#figure(
image("assets/3.png"),
supplement: [Рис.],
caption: [Результат выполнения @script2]
) <img3>
]
#align(center)[
#figure(
```sql
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "SALARY" = "SALARY" * 1.1
WHERE "EMPLOYEE_ID" = 101;
```,
supplement: [Скрипт.],
caption: [Повышение платы сотрудника на 10%.]
) <script3>
]
Обновляет поле "SALARY" у сотрудника с ID 101, увеличивая его значение в 1.1 раза внутри текущей транзакции. (@img4)
#align(center)[
#figure(
image("assets/4.png"),
supplement: [Рис.],
caption: [Результат выполнения @script3]
) <img4>
]
#align(center)[
#figure(
```sql
SELECT 'During' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 101;
```,
supplement: [Скрипт.],
caption: [Проверка значения внутри транзакции.]
) <script4>
]
Выводит обновлённые данные сотрудника с ID 101 во время выполнения транзакции, до фиксации изменений. (@img5)
#align(center)[
#figure(
image("assets/5.png"),
supplement: [Рис.],
caption: [Результат выполнения @script4]
) <img5>
]
#align(center)[
#figure(
```sql
COMMIT;
```,
supplement: [Скрипт.],
caption: [Фиксация изменений транзакции.]
) <script5>
]
Подтверждает все изменения, выполненные в текущей транзакции, делая их постоянными и видимыми для других сессий. (@img6)
#align(center)[
#figure(
image("assets/6.png"),
supplement: [Рис.],
caption: [Результат выполнения @script5]
) <img6>
]
#align(center)[
#figure(
```sql
SELECT 'After' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 101;
```,
supplement: [Скрипт.],
caption: [Проверка значения после фиксации транзакции.]
) <script6>
]
Выводит окончательные данные сотрудника с ID 101 после выполнения `COMMIT`, показывая зафиксированное изменение зарплаты. (@img7)
#align(center)[
#figure(
image("assets/7.png"),
supplement: [Рис.],
caption: [Результат выполнения @script6]
) <img7>
]
#align(center)[
#figure(
```sql
SELECT 'Before' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" <= 103
ORDER BY 2;
```,
supplement: [Скрипт.],
caption: [Выборка исходных значений сотрудников до начала транзакции.]
) <script7>
]
Получает текущие данные сотрудников с ID $lt.eq$ 103 для фиксации их состояния перед изменениями. (@img8)
#align(center)[
#figure(
image("assets/8.png"),
supplement: [Рис.],
caption: [Результат выполнения @script7]
) <img8>
]
#align(center)[
#figure(
```sql
BEGIN;
```,
supplement: [Скрипт.],
caption: [Начало транзакции.]
) <script8>
]
Открывает новую транзакцию, в пределах которой будут выполняться дальнейшие изменения. (@img9)
#align(center)[
#figure(
image("assets/9.png"),
supplement: [Рис.],
caption: [Результат выполнения @script8]
) <img9>
]
#align(center)[
#figure(
```sql
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "SALARY" = "SALARY" * 1.1
WHERE "EMPLOYEE_ID" <= 103;
```,
supplement: [Скрипт.],
caption: [Повышение зарплаты сотрудникам с ID $lt.eq$ 103 на 10%]
) <script9>
]
Временно обновляет зарплаты выбранных сотрудников внутри текущей транзакции, увеличивая их значения на 10%. (@img10)
#align(center)[
#figure(
image("assets/10.png"),
supplement: [Рис.],
caption: [Результат выполнения @script9]
) <img10>
]
#align(center)[
#figure(
```sql
SELECT 'Within transaction' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" <= 103
ORDER BY 2;
```,
supplement: [Скрипт.],
caption: [Просмотр изменённых данных внутри транзакции]
) <script10>
]
Выводит обновлённые зарплаты сотрудников с ID $lt.eq$ 103 до выполнения `ROLLBACK`, показывая состояние данных внутри незавершённой транзакции. (@img11)
#align(center)[
#figure(
image("assets/11.png"),
supplement: [Рис.],
caption: [Результат выполнения @script10]
) <img11>
]
#align(center)[
#figure(
```sql
ROLLBACK;
```,
supplement: [Скрипт.],
caption: [Откат транзакции]
) <script11>
]
Отменяет все изменения, сделанные в текущей транзакции, возвращая данные в состояние до `BEGIN`. (@img12)
#align(center)[
#figure(
image("assets/12.png"),
supplement: [Рис.],
caption: [Результат выполнения @script11]
) <img12>
]
#align(center)[
#figure(
```sql
SELECT 'After' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" <= 103
ORDER BY 2;
```,
supplement: [Скрипт.],
caption: [Проверка данных после отката транзакции]
) <script12>
]
Выводит состояние сотрудников с ID $lt.eq$ 103 после выполнения ROLLBACK, подтверждая, что изменения не были сохранены. (@img13)
#align(center)[
#figure(
image("assets/13.png"),
supplement: [Рис.],
caption: [Результат выполнения @script12]
) <img13>
]
#align(center)[
#figure(
```sql
BEGIN;
```,
supplement: [Скрипт.],
caption: [Начало новой транзакции.]
) <script13>
]
Открывает транзакционный блок, внутри которого будут выполняться операции с возможностью частичного отката через `SAVEPOINT`. (@img14)
#align(center)[
#figure(
image("assets/14.png"),
supplement: [Рис.],
caption: [Результат выполнения @script13]
) <img14>
]
#align(center)[
#figure(
```sql
SELECT 'Trans started' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 104;
```,
supplement: [Скрипт.],
caption: [Выборка исходных данных сотрудника перед работой с savepoint]
) <script14>
]
Получает текущее состояние сотрудника с ID 104 в момент начала транзакции, чтобы зафиксировать отправную точку перед изменениями. (@img15)
#align(center)[
#figure(
image("assets/15.png"),
supplement: [Рис.],
caption: [Результат выполнения @script14]
) <img15>
]
#align(center)[
#figure(
```sql
SAVEPOINT sp1;
```,
supplement: [Скрипт.],
caption: [Создание первой точки сохранения]
) <script15>
]
Устанавливает savepoint sp1 внутри текущей транзакции, позволяя откатить изменения до этой точки при необходимости. (@img16)
#align(center)[
#figure(
image("assets/16.png"),
supplement: [Рис.],
caption: [Результат выполнения @script15]
) <img16>
]
#align(center)[
#figure(
```sql
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "SALARY" = 3000
WHERE "EMPLOYEE_ID" = 104;
```,
supplement: [Скрипт.],
caption: [Установка новой зарплаты сотруднику после sp1]
) <script16>
]
Обновляет поле SALARY сотрудника с ID 104 до 3000 внутри текущей транзакции, после создания первой точки сохранения sp1. (@img17)
#align(center)[
#figure(
image("assets/17.png"),
supplement: [Рис.],
caption: [Результат выполнения @script16]
) <img17>
]
#align(center)[
#figure(
```sql
SELECT 'After SAVEP sp1' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 104;
```,
supplement: [Скрипт.],
caption: [Проверка данных после первой точки сохранения]
) <script17>
]
Выводит состояние сотрудника с ID 104 после обновления зарплаты до 3000, показывая результат после savepoint sp1. (@img18)
#align(center)[
#figure(
image("assets/18.png"),
supplement: [Рис.],
caption: [Результат выполнения @script17]
) <img18>
]
#align(center)[
#figure(
```sql
SAVEPOINT sp2;
```,
supplement: [Скрипт.],
caption: [Создание второй точки сохранения]
) <script18>
]
Устанавливает savepoint sp2 внутри текущей транзакции, чтобы при необходимости можно было откатить изменения только до этой новой точки. (@img19)
#align(center)[
#figure(
image("assets/19.png"),
supplement: [Рис.],
caption: [Результат выполнения @script18]
) <img19>
]
#align(center)[
#figure(
```sql
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "SALARY" = 10000
WHERE "EMPLOYEE_ID" = 104;
```,
supplement: [Скрипт.],
caption: [Установка новой зарплаты сотруднику после sp2]
) <script19>
]
Обновляет поле SALARY сотрудника с ID 104 до 10000 внутри текущей транзакции, после создания второй точки сохранения sp2. (@img20)
#align(center)[
#figure(
image("assets/20.png"),
supplement: [Рис.],
caption: [Результат выполнения @script19]
) <img20>
]
#align(center)[
#figure(
```sql
SELECT 'After SAVEP sp2' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 104;
```,
supplement: [Скрипт.],
caption: [Проверка данных после второй точки сохранения]
) <script20>
]
Выводит состояние сотрудника с ID 104 после обновления зарплаты до 10000, показывая результат после savepoint sp2. (@img21)
#align(center)[
#figure(
image("assets/21.png"),
supplement: [Рис.],
caption: [Результат выполнения @script20]
) <img21>
]
#align(center)[
#figure(
```sql
ROLLBACK TO SAVEPOINT sp2;
```,
supplement: [Скрипт.],
caption: [Откат к второй точке сохранения]
) <script21>
]
Отменяет все изменения, сделанные после savepoint sp2, возвращая данные к состоянию на момент его создания. (@img22)
#align(center)[
#figure(
image("assets/22.png"),
supplement: [Рис.],
caption: [Результат выполнения @script21]
) <img22>
]
#align(center)[
#figure(
```sql
SELECT 'After ROLLB sp2' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 104;
```,
supplement: [Скрипт.],
caption: [Проверка данных после отката к sp2]
) <script22>
]
Выводит состояние сотрудника с ID 104 после отката к savepoint sp2, показывая, что изменения после этой точки отменены. (@img23)
#align(center)[
#figure(
image("assets/23.png"),
supplement: [Рис.],
caption: [Результат выполнения @script22]
) <img23>
]
#align(center)[
#figure(
```sql
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "SALARY" = "SALARY" + 100
WHERE "EMPLOYEE_ID" = 104;
```,
supplement: [Скрипт.],
caption: [Увеличение зарплаты сотруднику после отката к sp2]
) <script23>
]
Добавляет 100 к текущей зарплате сотрудника с ID 104 внутри транзакции после отката к savepoint sp2. (@img24)
#align(center)[
#figure(
image("assets/24.png"),
supplement: [Рис.],
caption: [Результат выполнения @script23]
) <img24>
]
#align(center)[
#figure(
```sql
COMMIT;
```,
supplement: [Скрипт.],
caption: [Фиксация транзакции после изменений с savepoint]
) <script24>
]
Подтверждает все изменения, выполненные в транзакции, включая корректировку зарплаты после отката к savepoint, делая их постоянными и видимыми для других сессий. (@img25)
#align(center)[
#figure(
image("assets/25.png"),
supplement: [Рис.],
caption: [Результат выполнения @script24]
) <img25>
]
#align(center)[
#figure(
```sql
SELECT 'After trans' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 104;
```,
supplement: [Скрипт.],
caption: [Проверка данных после завершения транзакции]
) <script25>
]
Выводит окончательное состояние сотрудника с ID 104 после выполнения всех операций и фиксации транзакции, включая откат к savepoint и последующее увеличение зарплаты. (@img26)
#align(center)[
#figure(
image("assets/26.png"),
supplement: [Рис.],
caption: [Результат выполнения @script25]
) <img26>
]
==== Задание 2. Поиск и обнаружение блокировок
Я создал 3 вкладки в pgadmin: "Connection1", "Connection2", "Connection3". (@img27)
#align(center)[
#figure(
image("assets/27.png"),
supplement: [Рис.],
caption: [Вкладки "Connections"]
) <img27>
]
Во владке "Connection1" я выполнил @script26 (@img28)
#align(center)[
#figure(
```sql
DROP TABLE IF EXISTS public.t1;
CREATE TABLE public.t1 (
id int PRIMARY KEY,
price numeric(10, 2)
);
INSERT INTO public.t1 VALUES
(1, 10.00),
(2, 20.00),
(3, 30.00);
```,
supplement: [Скрипт.],
caption: [Создание тестовой таблицы с тремя записями.]
) <script26>
]
#align(center)[
#figure(
image("assets/28.png"),
supplement: [Рис.],
caption: [Выполнение @script26]
) <img28>
]
При помощи команды `SELECT current_database();` я проверил подключение к нужной базе. (@img29)
#align(center)[
#figure(
image("assets/29.png"),
supplement: [Рис.],
caption: [Проверка подключения.]
) <img29>
]
Затем я заблокировал первую строку при помощи @script27 (@img30)
#align(center)[
#figure(
```sql
BEGIN;
UPDATE public.t1
SET price = price + 1.00
WHERE id = 2;
SELECT 'Inside Connection 1' AS stage, id, price
FROM public.t1
WHERE id = 2;
```,
supplement: [Скрипт.],
caption: [Блокировка строки.]
) <script27>
]
Connection1 установит RowExclusiveLock на строку с id = 2 и удерживает его до `COMMIT` или `ROLLBACK`.
Пока транзакция открыта, блокировка активна.
#align(center)[
#figure(
image("assets/30.png"),
supplement: [Рис.],
caption: [Выполнение @script27]
) <img30>
]
Затем, из "Connection2" я попытался обновить ту же строку с помощью @script28 (@img31)
#align(center)[
#figure(
```sql
BEGIN;
SELECT id, price
FROM public.t1
WHERE id = 2;
UPDATE public.t1
SET price = 0
WHERE id = 2;
SELECT id, price
FROM public.t1
WHERE id = 2;
```,
supplement: [Скрипт.],
caption: [Обновление строки из "Connection2".]
) <script28>
]
#align(center)[
#figure(
image("assets/31.png"),
supplement: [Рис.],
caption: [Результат выполнения @script28]
) <img31>
]
Я получил ошибку, так как "Connection1" держит открытый `BEGIN` с `UPDATE` той же строки.
Затем, я проверил блокировки из "Connection3" при помощи @script29. (@img32)
#align(center)[
#figure(
```sql
SELECT pid,
usename AS username,
application_name,
state,
wait_event_type,
wait_event,
query
FROM pg_stat_activity
WHERE datname = current_database()
ORDER BY pid;
```,
supplement: [Скрипт.],
caption: [Проверка блокировок из "Connection3".]
) <script29>
]
#align(center)[
#figure(
image("assets/32.png"),
supplement: [Рис.],
caption: [Результат выполнения @script29]
) <img32>
]
- `pid` - идентификатор процесса.
- `state` - состояние сеанса.
- `wait_event_type` = Lock, если сеанс ждёт блокировку.
- `query` показывает последний выполняемый SQL.
Затем я провел детальный просмотр блокировок с `pg_locks` при помощи @script30. (@img33)
#align(center)[
#figure(
```sql
SELECT
l.pid,
a.application_name,
a.state,
l.locktype,
l.relation::regclass AS locked_relation,
l.page,
l.tuple,
l.virtualxid,
l.transactionid,
l.mode,
l.granted
FROM pg_locks l
LEFT JOIN pg_stat_activity a ON a.pid = l.pid
WHERE a.datname = current_database()
ORDER BY l.pid, l.locktype;
```,
supplement: [Скрипт.],
caption: [Просмотр блокировок с `pg_locks`.]
) <script30>
]
#align(center)[
#figure(
image("assets/33.png"),
supplement: [Рис.],
caption: [Результат выполнения @script30]
) <img33>
]
- `mode = RowExclusiveLock` - монопольная блокировка на строку.
- `granted = true` - блокировка удерживается; `false` - ждёт получения.
- `locked_relation` показывает таблицу, к которой применена блокировка.
- `tuple` и `page` идентификаторы строки и страницы.
Затем, при помощи `COMMIT` я завершил транзакцию в "Connection1". (@img34)
#align(center)[
#figure(
image("assets/34.png"),
supplement: [Рис.],
caption: [Завершение транзакции.]
) <img34>
]
Я повторил запросы @script29 и @script30 в "Connection3" чтобы проверить состояние после завершения транзакций. (@img35, @img36)
#align(center)[
#figure(
image("assets/35.png"),
supplement: [Рис.],
caption: [Результат выполнения @script29.]
) <img35>
]
#align(center)[
#figure(
image("assets/36.png"),
supplement: [Рис.],
caption: [Результат выполнения @script30.]
) <img36>
]
==== Задание 3. Уровени изоляции READ UNCOMMITTED и READ COMMITTED
Я создал две сессии с названиями "Session1" и "Session2". (@img37)
#align(center)[
#figure(
image("assets/37.png"),
supplement: [Рис.],
caption: [Сессии "Sessions".]
) <img37>
]
В "Session1" я начал транзакцию и изменение данных при помощи @script38. (@img38)
#align(center)[
#figure(
```sql
BEGIN;
SELECT id, price
FROM public.t1
WHERE id = 3;
UPDATE public.t1
SET price = 100
WHERE id = 3;
SELECT id, price
FROM public.t1
WHERE id = 3;
```,
supplement: [Скрипт.],
caption: [Блокировка строки.]
) <script38>
]
#align(center)[
#figure(
image("assets/38.png"),
supplement: [Рис.],
caption: [Результат выполнения @script38]
) <img38>
]
- На этом этапе строка с id = 3 в Session1 заблокирована монопольно.
- Любые другие `UPDATE` этой строки в других сессиях будут ждать `COMMIT` или `ROLLBACK`.
Затем в "Session2" я установил уровень изоляции `READ COMMITED` при помощи @script39. (@img39)
#align(center)[
#figure(
```sql
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT id, price
FROM public.t1
WHERE id = 3;
```,
supplement: [Скрипт.],
caption: [Установка уровня `READ COMMITED`.]
) <script39>
]
#align(center)[
#figure(
image("assets/39.png"),
supplement: [Рис.],
caption: [Результат выполнения @script39]
) <img39>
]
`READ COMMITTED` гарантирует, что каждая команда `SELECT` видит только зафиксированные изменения других транзакций.
Командой `COMMIT` в первой сессии я зафиксировал изменения. (@img40)
#align(center)[
#figure(
image("assets/40.png"),
supplement: [Рис.],
caption: [Фиксирование изменений.]
) <img40>
]
Теперь транзакция Session1 завершена. Монопольная блокировка снята, а новые данные зафиксированы.
Затем я провел повторное чтение в "Session2" при помощи @script41. (@img41)
#align(center)[
#figure(
```sql
SELECT id, price
FROM public.t1
WHERE id = 3;
```,
supplement: [Скрипт.],
caption: [Чтение в "Session2".]
) <script41>
]
#align(center)[
#figure(
image("assets/41.png"),
supplement: [Рис.],
caption: [Результат использования @script41.]
) <img41>
]
После этого, я завершил транзакцию в "Session2" при помощи команды `COMMIT;`. (@img42)
#align(center)[
#figure(
image("assets/42.png"),
supplement: [Рис.],
caption: [Закрытие транзакции в "Session2".]
) <img42>
]
==== Задание 4. Уровень изоляции REPEATABLE READ
В "Session1" я запустил транзакцию `REPEATABLE READ` при помощи @script43. (@img43)
#align(center)[
#figure(
```sql
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT id, price
FROM public.t1
WHERE id = 3;
```,
supplement: [Скрипт.],
caption: [Запуск транзакции в "Session1".]
) <script43>
]
#align(center)[
#figure(
image("assets/43.png"),
supplement: [Рис.],
caption: [Результат выполнения @script43.]
) <img43>
]
Затем в "Session2" я попытался изменить ту же строку при помощи @script44. (@img44)
#align(center)[
#figure(
```sql
BEGIN;
UPDATE public.t1
SET price = 1
WHERE id = 3;
COMMIT;
```,
supplement: [Скрипт.],
caption: [Изменение строки в "Session2".]
) <script44>
]
#align(center)[
#figure(
image("assets/44.png"),
supplement: [Рис.],
caption: [Результат выполнения @script44.]
) <img44>
]
- `UPDATE` в "Session2" получит монопольную блокировку строки.
- Сессия "Session1" держит совместную блокировку, но она не мешает `UPDATE`.
- После `COMMIT` изменения вступают в силу для других будущих транзакций, но не для "Session1".
После, я повторил `SELECT` в "Session1" при помощи @script45. (@img45)
#align(center)[
#figure(
```sql
SELECT id, price
FROM public.t1
WHERE id = 3;
COMMIT;
```,
supplement: [Скрипт.],
caption: [Повторный `SELECT` в "Session1".]
) <script45>
]
#align(center)[
#figure(
image("assets/45.png"),
supplement: [Рис.],
caption: [Результат выполнения @script45.]
) <img45>
]
Результат остался тот, который был на момент начала транзакции "Session1".
Для демонстрации отсутствия фантомов, я начал новую транзакцию в "Session1" при помощи @script46. (@img46)
#align(center)[
#figure(
```sql
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT id, price
FROM public.t1
WHERE price = 1;
```,
supplement: [Скрипт.],
caption: [Начало транзакции в "Session1"]
) <script46>
]
#align(center)[
#figure(
image("assets/46.png"),
supplement: [Рис.],
caption: [Результат выполнения @script46.]
) <img46>
]
В "Session2" добавил новую строку при помощи @script47. (@img47)
#align(center)[
#figure(
```sql
BEGIN;
INSERT INTO public.t1 VALUES (4, 1);
COMMIT;
```,
supplement: [Скрипт.],
caption: [Добавление новой строки.]
) <script47>
]
#align(center)[
#figure(
image("assets/47.png"),
supplement: [Рис.],
caption: [Результат выполнения @script47.]
) <img47>
]
Затем повторил `SELECT` для "Session1" при помощи @script48. (@img48)
#align(center)[
#figure(
```sql
SELECT id, price
FROM public.t1
WHERE price = 1;
COMMIT;
```,
supplement: [Скрипт.],
caption: [Повторный `SELECT` для "Session1".]
) <script48>
]
#align(center)[
#figure(
image("assets/48.png"),
supplement: [Рис.],
caption: [Результат выполнения @script48.]
) <img48>
]
==== Задание 5. Уровень изоляции SERIALIZABLE
Я обновил строку в "Session1" и прочитал ее при помощи @script49. (@img49)
#align(center)[
#figure(
```sql
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE public.t1
SET price = 111
WHERE id = 1;
SELECT id, price
FROM public.t1
WHERE id = 1;
```,
supplement: [Скрипт.],
caption: [Обновление и чтение строки в "Session1".]
) <script49>
]
#align(center)[
#figure(
image("assets/49.png"),
supplement: [Рис.],
caption: [Результат выполнения @script49]
) <img49>
]
У транзакции "Session1" теперь есть монопольная блокировка на строку id=1 до `COMMIT`.
Затем из "Session2" я попытался обновить ту же строку при помощи @script50. (@img50)
#align(center)[
#figure(
```sql
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE public.t1
SET price = 222
WHERE id = 1;
COMMIT;
```,
supplement: [Скрипт.],
caption: [Попытка обновления строки из "Session2".]
) <script50>
]
#align(center)[
#figure(
image("assets/50.png"),
supplement: [Рис.],
caption: [Результат выполнения @script50.]
) <img50>
]
Сделал `SELECT` и `COMMIT` в "Session1" при помощи @script51. (@img51)
#align(center)[
#figure(
```sql
SELECT id, price
FROM public.t1
WHERE id = 1;
COMMIT;
```,
supplement: [Скрипт.],
caption: [`SELECT` и `COMMIT` в "Session1".]
) <script51>
]
#align(center)[
#figure(
image("assets/51.png"),
supplement: [Рис.],
caption: [Результат выполнения @script51]
) <img51>
]
=== Выводы и анализ результатов работы
// Обобщение результатов выполнения всех задач работы: что должны были достичь, что фактически достигли и каким образом, с какими трудностями столкнулись, какие проблемы на каких этапах выполнения возникли и как именно были решены.
В процессе выполнения работы были изучены основные механизмы транзакций postgresql: фиксация и откат изменений, работа с точками сохранения, а также влияние ошибок на состояние транзакции. На практике подтверждено, что изменения внутри транзакции не видны другим сессиям до выполнения `COMMIT`, а любая ошибка переводит транзакцию в состояние aborted, после чего дальнейшие команды игнорируются до выполнения `ROLLBACK`.
Также было проверено поведение нескольких подключений к базе: конкурентные транзакции блокируют доступ к одним и тем же строкам, что обеспечивает целостность данных. Работа с `SAVEPOINT` показала возможность частичного отката без потери всей транзакции.
Основные трудности были связаны с ошибками из-за неверной схемы таблиц и пониманием состояния aborted, но они были устранены в процессе выполнения. В итоге цели работы полностью достигнуты.