#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: [Выключение автокоммитов.] ) ] Я открыл "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: [Выборка данных сотрудника до начала транзакции.] ) ] Запрашивает текущее значение полей сотрудника с ID 101, чтобы зафиксировать исходное состояние перед выполнением изменений в транзакции. #align(center)[ #figure( image("assets/2.png"), supplement: [Рис.], caption: [Результат выполнения @script1] ) ] #align(center)[ #figure( ```sql BEGIN; ```, supplement: [Скрипт.], caption: [Начало транзакции.] ) ] Запускает новую транзакцию, в рамках которой дальнейшие операции будут выполняться атомарно и не будут видны другим сессиям до `COMMIT` или `ROLLBACK`. #align(center)[ #figure( image("assets/3.png"), supplement: [Рис.], caption: [Результат выполнения @script2] ) ] #align(center)[ #figure( ```sql UPDATE "EmployeesDepartments"."EMPLOYEES" SET "SALARY" = "SALARY" * 1.1 WHERE "EMPLOYEE_ID" = 101; ```, supplement: [Скрипт.], caption: [Повышение платы сотрудника на 10%.] ) ] Обновляет поле "SALARY" у сотрудника с ID 101, увеличивая его значение в 1.1 раза внутри текущей транзакции. (@img4) #align(center)[ #figure( image("assets/4.png"), supplement: [Рис.], caption: [Результат выполнения @script3] ) ] #align(center)[ #figure( ```sql SELECT 'During' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY" FROM "EmployeesDepartments"."EMPLOYEES" WHERE "EMPLOYEE_ID" = 101; ```, supplement: [Скрипт.], caption: [Проверка значения внутри транзакции.] ) ] Выводит обновлённые данные сотрудника с ID 101 во время выполнения транзакции, до фиксации изменений. (@img5) #align(center)[ #figure( image("assets/5.png"), supplement: [Рис.], caption: [Результат выполнения @script4] ) ] #align(center)[ #figure( ```sql COMMIT; ```, supplement: [Скрипт.], caption: [Фиксация изменений транзакции.] ) ] Подтверждает все изменения, выполненные в текущей транзакции, делая их постоянными и видимыми для других сессий. (@img6) #align(center)[ #figure( image("assets/6.png"), supplement: [Рис.], caption: [Результат выполнения @script5] ) ] #align(center)[ #figure( ```sql SELECT 'After' AS stage, "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY" FROM "EmployeesDepartments"."EMPLOYEES" WHERE "EMPLOYEE_ID" = 101; ```, supplement: [Скрипт.], caption: [Проверка значения после фиксации транзакции.] ) ] Выводит окончательные данные сотрудника с ID 101 после выполнения `COMMIT`, показывая зафиксированное изменение зарплаты. (@img7) #align(center)[ #figure( image("assets/7.png"), supplement: [Рис.], caption: [Результат выполнения @script6] ) ] #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: [Выборка исходных значений сотрудников до начала транзакции.] ) ] Получает текущие данные сотрудников с ID $lt.eq$ 103 для фиксации их состояния перед изменениями. (@img8) #align(center)[ #figure( image("assets/8.png"), supplement: [Рис.], caption: [Результат выполнения @script7] ) ] #align(center)[ #figure( ```sql BEGIN; ```, supplement: [Скрипт.], caption: [Начало транзакции.] ) ] Открывает новую транзакцию, в пределах которой будут выполняться дальнейшие изменения. (@img9) #align(center)[ #figure( image("assets/9.png"), supplement: [Рис.], caption: [Результат выполнения @script8] ) ] #align(center)[ #figure( ```sql UPDATE "EmployeesDepartments"."EMPLOYEES" SET "SALARY" = "SALARY" * 1.1 WHERE "EMPLOYEE_ID" <= 103; ```, supplement: [Скрипт.], caption: [Повышение зарплаты сотрудникам с ID $lt.eq$ 103 на 10%] ) ] Временно обновляет зарплаты выбранных сотрудников внутри текущей транзакции, увеличивая их значения на 10%. (@img10) #align(center)[ #figure( image("assets/10.png"), supplement: [Рис.], caption: [Результат выполнения @script9] ) ] #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: [Просмотр изменённых данных внутри транзакции] ) ] Выводит обновлённые зарплаты сотрудников с ID $lt.eq$ 103 до выполнения `ROLLBACK`, показывая состояние данных внутри незавершённой транзакции. (@img11) #align(center)[ #figure( image("assets/11.png"), supplement: [Рис.], caption: [Результат выполнения @script10] ) ] #align(center)[ #figure( ```sql ROLLBACK; ```, supplement: [Скрипт.], caption: [Откат транзакции] ) ] Отменяет все изменения, сделанные в текущей транзакции, возвращая данные в состояние до `BEGIN`. (@img12) #align(center)[ #figure( image("assets/12.png"), supplement: [Рис.], caption: [Результат выполнения @script11] ) ] #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: [Проверка данных после отката транзакции] ) ] Выводит состояние сотрудников с ID $lt.eq$ 103 после выполнения ROLLBACK, подтверждая, что изменения не были сохранены. (@img13) #align(center)[ #figure( image("assets/13.png"), supplement: [Рис.], caption: [Результат выполнения @script12] ) ] #align(center)[ #figure( ```sql BEGIN; ```, supplement: [Скрипт.], caption: [Начало новой транзакции.] ) ] Открывает транзакционный блок, внутри которого будут выполняться операции с возможностью частичного отката через `SAVEPOINT`. (@img14) #align(center)[ #figure( image("assets/14.png"), supplement: [Рис.], caption: [Результат выполнения @script13] ) ] #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] ) ] Получает текущее состояние сотрудника с ID 104 в момент начала транзакции, чтобы зафиксировать отправную точку перед изменениями. (@img15) #align(center)[ #figure( image("assets/15.png"), supplement: [Рис.], caption: [Результат выполнения @script14] ) ] #align(center)[ #figure( ```sql SAVEPOINT sp1; ```, supplement: [Скрипт.], caption: [Создание первой точки сохранения] ) ] Устанавливает savepoint sp1 внутри текущей транзакции, позволяя откатить изменения до этой точки при необходимости. (@img16) #align(center)[ #figure( image("assets/16.png"), supplement: [Рис.], caption: [Результат выполнения @script15] ) ] #align(center)[ #figure( ```sql UPDATE "EmployeesDepartments"."EMPLOYEES" SET "SALARY" = 3000 WHERE "EMPLOYEE_ID" = 104; ```, supplement: [Скрипт.], caption: [Установка новой зарплаты сотруднику после sp1] ) ] Обновляет поле SALARY сотрудника с ID 104 до 3000 внутри текущей транзакции, после создания первой точки сохранения sp1. (@img17) #align(center)[ #figure( image("assets/17.png"), supplement: [Рис.], caption: [Результат выполнения @script16] ) ] #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: [Проверка данных после первой точки сохранения] ) ] Выводит состояние сотрудника с ID 104 после обновления зарплаты до 3000, показывая результат после savepoint sp1. (@img18) #align(center)[ #figure( image("assets/18.png"), supplement: [Рис.], caption: [Результат выполнения @script17] ) ] #align(center)[ #figure( ```sql SAVEPOINT sp2; ```, supplement: [Скрипт.], caption: [Создание второй точки сохранения] ) ] Устанавливает savepoint sp2 внутри текущей транзакции, чтобы при необходимости можно было откатить изменения только до этой новой точки. (@img19) #align(center)[ #figure( image("assets/19.png"), supplement: [Рис.], caption: [Результат выполнения @script18] ) ] #align(center)[ #figure( ```sql UPDATE "EmployeesDepartments"."EMPLOYEES" SET "SALARY" = 10000 WHERE "EMPLOYEE_ID" = 104; ```, supplement: [Скрипт.], caption: [Установка новой зарплаты сотруднику после sp2] ) ] Обновляет поле SALARY сотрудника с ID 104 до 10000 внутри текущей транзакции, после создания второй точки сохранения sp2. (@img20) #align(center)[ #figure( image("assets/20.png"), supplement: [Рис.], caption: [Результат выполнения @script19] ) ] #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: [Проверка данных после второй точки сохранения] ) ] Выводит состояние сотрудника с ID 104 после обновления зарплаты до 10000, показывая результат после savepoint sp2. (@img21) #align(center)[ #figure( image("assets/21.png"), supplement: [Рис.], caption: [Результат выполнения @script20] ) ] #align(center)[ #figure( ```sql ROLLBACK TO SAVEPOINT sp2; ```, supplement: [Скрипт.], caption: [Откат к второй точке сохранения] ) ] Отменяет все изменения, сделанные после savepoint sp2, возвращая данные к состоянию на момент его создания. (@img22) #align(center)[ #figure( image("assets/22.png"), supplement: [Рис.], caption: [Результат выполнения @script21] ) ] #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] ) ] Выводит состояние сотрудника с ID 104 после отката к savepoint sp2, показывая, что изменения после этой точки отменены. (@img23) #align(center)[ #figure( image("assets/23.png"), supplement: [Рис.], caption: [Результат выполнения @script22] ) ] #align(center)[ #figure( ```sql UPDATE "EmployeesDepartments"."EMPLOYEES" SET "SALARY" = "SALARY" + 100 WHERE "EMPLOYEE_ID" = 104; ```, supplement: [Скрипт.], caption: [Увеличение зарплаты сотруднику после отката к sp2] ) ] Добавляет 100 к текущей зарплате сотрудника с ID 104 внутри транзакции после отката к savepoint sp2. (@img24) #align(center)[ #figure( image("assets/24.png"), supplement: [Рис.], caption: [Результат выполнения @script23] ) ] #align(center)[ #figure( ```sql COMMIT; ```, supplement: [Скрипт.], caption: [Фиксация транзакции после изменений с savepoint] ) ] Подтверждает все изменения, выполненные в транзакции, включая корректировку зарплаты после отката к savepoint, делая их постоянными и видимыми для других сессий. (@img25) #align(center)[ #figure( image("assets/25.png"), supplement: [Рис.], caption: [Результат выполнения @script24] ) ] #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: [Проверка данных после завершения транзакции] ) ] Выводит окончательное состояние сотрудника с ID 104 после выполнения всех операций и фиксации транзакции, включая откат к savepoint и последующее увеличение зарплаты. (@img26) #align(center)[ #figure( image("assets/26.png"), supplement: [Рис.], caption: [Результат выполнения @script25] ) ] ==== Задание 2. Поиск и обнаружение блокировок Я создал 3 вкладки в pgadmin: "Connection1", "Connection2", "Connection3". (@img27) #align(center)[ #figure( image("assets/27.png"), supplement: [Рис.], caption: [Вкладки "Connections"] ) ] Во владке "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: [Создание тестовой таблицы с тремя записями.] ) ] #align(center)[ #figure( image("assets/28.png"), supplement: [Рис.], caption: [Выполнение @script26] ) ] При помощи команды `SELECT current_database();` я проверил подключение к нужной базе. (@img29) #align(center)[ #figure( image("assets/29.png"), supplement: [Рис.], caption: [Проверка подключения.] ) ] Затем я заблокировал первую строку при помощи @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: [Блокировка строки.] ) ] Connection1 установит RowExclusiveLock на строку с id = 2 и удерживает его до `COMMIT` или `ROLLBACK`. Пока транзакция открыта, блокировка активна. #align(center)[ #figure( image("assets/30.png"), supplement: [Рис.], caption: [Выполнение @script27] ) ] Затем, из "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".] ) ] #align(center)[ #figure( image("assets/31.png"), supplement: [Рис.], caption: [Результат выполнения @script28] ) ] Я получил ошибку, так как "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".] ) ] #align(center)[ #figure( image("assets/32.png"), supplement: [Рис.], caption: [Результат выполнения @script29] ) ] - `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`.] ) ] #align(center)[ #figure( image("assets/33.png"), supplement: [Рис.], caption: [Результат выполнения @script30] ) ] - `mode = RowExclusiveLock` - монопольная блокировка на строку. - `granted = true` - блокировка удерживается; `false` - ждёт получения. - `locked_relation` показывает таблицу, к которой применена блокировка. - `tuple` и `page` — идентификаторы строки и страницы. Затем, при помощи `COMMIT` я завершил транзакцию в "Connection1". (@img34) #align(center)[ #figure( image("assets/34.png"), supplement: [Рис.], caption: [Завершение транзакции.] ) ] Я повторил запросы @script29 и @script30 в "Connection3" чтобы проверить состояние после завершения транзакций. (@img35, @img36) #align(center)[ #figure( image("assets/35.png"), supplement: [Рис.], caption: [Результат выполнения @script29.] ) ] #align(center)[ #figure( image("assets/36.png"), supplement: [Рис.], caption: [Результат выполнения @script30.] ) ] ==== Задание 3. Уровени изоляции READ UNCOMMITTED и READ COMMITTED Я создал две сессии с названиями "Session1" и "Session2". (@img37) #align(center)[ #figure( image("assets/37.png"), supplement: [Рис.], caption: [Сессии "Sessions".] ) ] В "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: [Блокировка строки.] ) ] #align(center)[ #figure( image("assets/38.png"), supplement: [Рис.], caption: [Результат выполнения @script38] ) ] - На этом этапе строка с 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`.] ) ] #align(center)[ #figure( image("assets/39.png"), supplement: [Рис.], caption: [Результат выполнения @script39] ) ] `READ COMMITTED` гарантирует, что каждая команда `SELECT` видит только зафиксированные изменения других транзакций. Командой `COMMIT` в первой сессии я зафиксировал изменения. (@img40) #align(center)[ #figure( image("assets/40.png"), supplement: [Рис.], caption: [Фиксирование изменений.] ) ] Теперь транзакция Session1 завершена. Монопольная блокировка снята, а новые данные зафиксированы. Затем я провел повторное чтение в "Session2" при помощи @script41. (@img41) #align(center)[ #figure( ```sql SELECT id, price FROM public.t1 WHERE id = 3; ```, supplement: [Скрипт.], caption: [Чтение в "Session2".] ) ] #align(center)[ #figure( image("assets/41.png"), supplement: [Рис.], caption: [Результат использования @script41.] ) ] После этого, я завершил транзакцию в "Session2" при помощи команды `COMMIT;`. (@img42) #align(center)[ #figure( image("assets/42.png"), supplement: [Рис.], caption: [Закрытие транзакции в "Session2".] ) ] ==== Задание 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".] ) ] #align(center)[ #figure( image("assets/43.png"), supplement: [Рис.], caption: [Результат выполнения @script43.] ) ] Затем в "Session2" я попытался изменить ту же строку при помощи @script44. (@img44) #align(center)[ #figure( ```sql BEGIN; UPDATE public.t1 SET price = 1 WHERE id = 3; COMMIT; ```, supplement: [Скрипт.], caption: [Изменение строки в "Session2".] ) ] #align(center)[ #figure( image("assets/44.png"), supplement: [Рис.], caption: [Результат выполнения @script44.] ) ] - `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".] ) ] #align(center)[ #figure( image("assets/45.png"), supplement: [Рис.], caption: [Результат выполнения @script45.] ) ] Результат остался тот, который был на момент начала транзакции "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"] ) ] #align(center)[ #figure( image("assets/46.png"), supplement: [Рис.], caption: [Результат выполнения @script46.] ) ] В "Session2" добавил новую строку при помощи @script47. (@img47) #align(center)[ #figure( ```sql BEGIN; INSERT INTO public.t1 VALUES (4, 1); COMMIT; ```, supplement: [Скрипт.], caption: [Добавление новой строки.] ) ] #align(center)[ #figure( image("assets/47.png"), supplement: [Рис.], caption: [Результат выполнения @script47.] ) ] Затем повторил `SELECT` для "Session1" при помощи @script48. (@img48) #align(center)[ #figure( ```sql SELECT id, price FROM public.t1 WHERE price = 1; COMMIT; ```, supplement: [Скрипт.], caption: [Повторный `SELECT` для "Session1".] ) ] #align(center)[ #figure( image("assets/48.png"), supplement: [Рис.], caption: [Результат выполнения @script48.] ) ] ==== Задание 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".] ) ] #align(center)[ #figure( image("assets/49.png"), supplement: [Рис.], caption: [Результат выполнения @script49] ) ] У транзакции "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".] ) ] #align(center)[ #figure( image("assets/50.png"), supplement: [Рис.], caption: [Результат выполнения @script50.] ) ] Сделал `SELECT` и `COMMIT` в "Session1" при помощи @script51. (@img51) #align(center)[ #figure( ```sql SELECT id, price FROM public.t1 WHERE id = 1; COMMIT; ```, supplement: [Скрипт.], caption: [`SELECT` и `COMMIT` в "Session1".] ) ] #align(center)[ #figure( image("assets/51.png"), supplement: [Рис.], caption: [Результат выполнения @script51] ) ] === Выводы и анализ результатов работы // Обобщение результатов выполнения всех задач работы: что должны были достичь, что фактически достигли и каким образом, с какими трудностями столкнулись, какие проблемы на каких этапах выполнения возникли и как именно были решены. В процессе выполнения работы были изучены основные механизмы транзакций postgresql: фиксация и откат изменений, работа с точками сохранения, а также влияние ошибок на состояние транзакции. На практике подтверждено, что изменения внутри транзакции не видны другим сессиям до выполнения `COMMIT`, а любая ошибка переводит транзакцию в состояние aborted, после чего дальнейшие команды игнорируются до выполнения `ROLLBACK`. Также было проверено поведение нескольких подключений к базе: конкурентные транзакции блокируют доступ к одним и тем же строкам, что обеспечивает целостность данных. Работа с `SAVEPOINT` показала возможность частичного отката без потери всей транзакции. Основные трудности были связаны с ошибками из-за неверной схемы таблиц и пониманием состояния aborted, но они были устранены в процессе выполнения. В итоге цели работы полностью достигнуты.