1223 lines
37 KiB
Typst
1223 lines
37 KiB
Typst
#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, но они были устранены в процессе выполнения. В итоге цели работы полностью достигнуты.
|
||
|