-- Транзакция - связанный набор операций -- -- Atomicity Атомарность, выполняется либо целиком, либо целиком откатывается/отменяется -- Вася Петя -- 20 10 -- 20-10 -- СБОЙ СИСТЕМЫ -- 10+10 -- Consistency Консистентность, после завершения транзакции система должна оказаться в непротиворечивом состоянии -- Граф A <-> B -- NodeID (not null), ParentId (not null, FK NodeId) -- BEGIN -- A Null (Запрещено ключами) -- B A -- UPDATE table SET ParentID = B where NodeID = A -- COMMIT -- Isolated Изолированность - Транзации происходящие одновременно с текущей не должны влиять -- Durable Надежность – Как только произошел коммит, никакой сбой произошедший после коммита не должен на него повлиять -- Atomicity транзакция выполняется либо полностью либо откатывается целиком -- Consistency если база данных находится в консистентном состоянии (все инварианты выполнены), -- то после транзакции база останется в консистентном состоянии -- внутри транзакции консистентность может нарушаться -- Isolation эффекты одних транзакций не должны влиять на другие транзакции -- Durability эффекты от завершенной транзакции должны быть устойчивыми -- Transaction последовательность операций либо выполняется либо отклонояется -- Инварианты: -- Суммарный баланс не должен меняться -- Баланс >= 0 drop table if exists account; create table account(client varchar(100) primary key, balance int); insert into account values ('vasya', 100), ('petya', 200); select * from account; -- do check in application petya's balance >= 100 -- BEGIN; update account set balance = balance - 100 where client = 'petya'; update account set balance = balance + 100 where client = 'vasya'; -- COMMIT; -- ROLLBACK; select * from account; -- Race Condition (Lost Update) -- Connect1 Connect2 -- read petya read petya -- check petya balance check petya balance -- petya -100 -- write 100 -- petya -200 -- write 0 BEGIN; select * from account; UPDATE account set balance = balance + 10 where client = 'petya'; select * from account; ROLLBACK; -- Одновременно во втором коннекте НАЧАЛО BEGIN; select * from account; UPDATE account set balance = balance + 20 where client = 'petya'; select * from account; COMMIT; -- Одновременно во втором коннекте КОНЕЦ -- Option 1 прервать -- Option 2 перезапустить -- Deadlock select * from account; BEGIN; UPDATE account set balance = balance + 10 where client = 'petya'; select * from account; UPDATE account set balance = balance - 10 where client = 'vasya'; select * from account; COMMIT; -- Одновременно во втором коннекте НАЧАЛО select * from account; BEGIN; UPDATE account set balance = balance - 10 where client = 'vasya'; select * from account; UPDATE account set balance = balance + 10 where client = 'petya'; COMMIT; -- Одновременно во втором коннекте КОНЕЦ -- Lock, Unlock, Read, Write -- p == petya -- v == vasya -- T1 T2 -- lock(p) -- lock(v) -- read(p) -- read(v) -- write(p) -- write(v) -- lock(v) <- Ждём T2 -- lock(p) <- Ждём Т1 -- обнаружена блокировка -- rollback T2: -- unlock(v) -- блокировка v успешна -- read(v) -- write(v) -- commit: -- unlock(v) -- unlock(p) -- Список Блокировок -- T1: p -- T2: v -- Граф ожидания -- T1 <-> T2 (цикл в графе) -- Процесс поиска блокировок периодически строит граф и ищет в нём циклы -- Read-Write lock -- R W -- R + - -- W - - -- CAP-theorem consistency/availability/partition-tolerance -- BASE - Basically Available, Soft state, Eventually Consistent -- stale/fair use se; show schemas; show tables; # Cycle drop table if exists graph; create table graph( id INT primary key, parent_id INT not null, FOREIGN KEY (parent_id) references graph(id) ); select * from graph; # 1, 2 # 2, 1 insert into graph values (1, 2); insert into graph values (2, 1); insert into graph values (1, 2), (2, 1); insert into graph values (1, NULL); insert into graph values (2, 1); UPDATE graph set parent_id = 2 where id = 1; insert into graph values (1, 1); insert into graph values (2, 1); UPDATE graph set parent_id = 2 where id = 1; select * from graph; -- mysql not support deferred checks set foreign_key_checks = 0; BEGIN; insert into graph values (1, 2); insert into graph values (2, 1); COMMIT; set foreign_key_checks = 1; -- copy from console 2 # use se; # SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; # BEGIN; # select * from t; # ROLLBACK; # # SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; # BEGIN; # UPDATE t set b = b + 1 WHERE a = 1; # COMMIT; # # SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; # BEGIN; # insert into t values (3, 3); # select * from t; # COMMIT; # # SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; # BEGIN; # insert into t values (4, 4); # select * from t; # COMMIT; show schemas; create schema if not exists se; use se; drop table if exists t; create table t (a int primary key, b int); insert into t values (1, 1), (2, 2); select * from t; -- READ UNCOMMITTED -- READ COMMITTED -- REPEATABLE READ -- SERIALIZABLE -- Isolation Level Dirty Read Nonrepeatable Read Phantom Read Serialization Anomaly -- Read uncommitted Allowed, but not in PG Possible Possible Possible -- Read committed Not possible Possible Possible Possible -- Repeatable read Not possible Not possible Allowed, but not in PG Possible -- Serializable Not possible Not possible Not possible Not possible -- Lost Update (невозможен) -- T1 T2 -- BEING -- b = b + 1 where a = 1 BEGIN -- b = b + 2 where a = 1 -- COMMIT -- COMMIT -- Первый пользователь попытался купить билет -- Второй пользователь в этот момент увидел что билет куплен -- У первого пользователя не хватило денег и он отказался -- Dirty Read -- T1 T2 -- b = b + 1 where a = 1 -- select b -- rollback SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; BEGIN; UPDATE t set b = b + 1 WHERE a = 1; select * from t; ROLLBACK; show processlist; kill connection 19; kill query 19; -- Non repeatable read -- T1 T2 -- select b -- b = b + 1 where a = 1 -- select b -- rollback SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; select * from t; -- update in t2 select * from t; ROLLBACK; -- Phantom read -- T1 T2 -- select b -- insert b (3, 3) -- select b -- rollback SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; BEGIN; -- select * from t; -- insert in t2 -- select * from t; -- mysql does not allow phantom reads, but allow phantom writes select * from t; -- insert in t2 update t set b = 10 where a = 3; select * from t; COMMIT; -- Самая строгая изоляция SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN; select * from t; update t set b = 3 where a = 3; select * from t; COMMIT; -- Нормальные формы -- Дублирование -- Аномалии DML -- Неконсистентность -- 1НФ -- Нет повторений -- Все значения скалярны drop table if exists cars; create table cars (brand varchar(100), model varchar(100)); insert into cars values ('BMW', 'X5, X7'), ('NISSAN', 'GT-R'), ('NISSAN', 'GT-R'); select * from cars; drop table if exists cars; create table cars (brand varchar(100), model varchar(100), primary key (brand, model)); insert into cars values ('BMW', 'X5'), ('BMW', 'X7'), # ('NISSAN', 'GT-R'), ('NISSAN', 'GT-R'); select * from cars; -- 2НФ -- 1НФ + -- Каждый неключевой атрибут неприводимо зависит от каждого потенциального кллюча -- (схемы с простыми ключами всегда во 2НФ) -- Аномалии: -- Избыточность -- Нельзя создать склад без деталей -- Удаление детали приводит к потере склада drop table if exists store; create table store ( store varchar(100), detail varchar(100), count int, address varchar(100), primary key (store, detail)); insert into store values ('main', 'monitor', 10, 'bakinsky 15'), ('main', 'laptop', 10, 'bakinsky 15'), ('aux', 'mouse', 1, 'kuznetsky 18'); select * from store; -- Разбиваем составной ключ drop table if exists stores; create table stores ( store varchar(100) primary key, address varchar(100)); insert into stores values ('main', 'bakinsky 15'), ('aux', 'kuznetsky 18'); drop table if exists details; create table details( store varchar(100), detail varchar(100), count int, primary key (store, detail), foreign key (store) references stores(store) ); insert into details values ('main', 'monitor', 10), ('main', 'laptop', 10), ('aux', 'mouse', 1); select * from stores; select * from details; select * from stores s join details d on s.store = d.store; -- 3НФ -- 2НФ + -- Ни один неключевой отрибут не находится в зависимости от потенциального ключа -- (Есть транзитивные зависимости между атрибутами) drop table if exists employee; create table employee (name varchar(100) primary key, department varchar(100), phone varchar(100)); insert into employee values ('vasya@example.com', 'development', '101'), ('petya@example.com', 'development', '101'), ('kolya@example.com', 'qa', '103'); select * from employee; -- НФБК 3+НФ Бойс Кодд -- Более строгая 3НФ -- Каждая нетривиальная неприводимая слева функциональная зависимость -- имеет в качестве своего детерминанта некоторый потенциальный ключ -- (Нет зависимостей внутри ключа) drop table if exists supplier; create table supplier( code int, supplier varchar(100), detail varchar(100), count int, primary key (code, supplier, detail)); insert into supplier values (1, 'gl', 'internet', 100), (1, 'gl', 'tv', 10), (2, 'papa', 'pizza', 5); select * from supplier; -- 4НФ -- НФБК + -- Все нетривиальные многозначные зависимости фактически являются функциональными зависимостями от потенциальных ключей -- Аномалии: -- При добавлении пиццы или района необходимо добавить все районы/пиццы/рестораны -- При удалении теряется информация от районах/пиццах drop table if exists pizza; create table pizza ( restraunt varchar(100), pizza varchar(100), district varchar(100), primary key (restraunt, pizza, district)); insert into pizza values ('sovetsky', 'pepperoni', 'central'), ('sovetsky', 'margarita', 'central'), ('raduga', 'margarita', 'kirovsky'), ('raduga', 'margarita', 'rudnichny'); select * from pizza; -- Пиццы -- Рестораны -- delivery, rest_district Ресторан -> Районы доставки -- assort Ресторан -> Пиццы -- order (delivery_id, assort_id, 10) -- 5НФ -- 4НФ + -- Отсутствуют сложные соединения между атррибутами -- Аномалии: -- Продавец имеет право торговать только с определенными фирамами -- данное отношение не может исключать ситуации когода отношение нарушается drop table if exists seller; create table seller ( seller varchar(100), supplier varchar(100), product varchar(100), primary key (seller, supplier, product)); insert into seller values ('ya', 'bosch', 'refrigerator'), ('ya', 'bosch', 'cooker'), ('ya', 'lg', 'vacuum'), ('promenad', 'bosch', 'washing machine'), ('promenad', 'lg', 'vacuum'); select * from seller; -- Необхоимо разбить на три отношения -- ДКНФ -- Каждое наложенное ограничение является логическим следствием ограничений доменов и -- ограничений ключей наложенных на данную переменную отношения -- По-сути задание перечня допустимых значений для каждого типа -- 6НФ -- 5НФ + -- Переменная отношения находится в 6НФ тогда и только тогда когда удовлетворяет всем нетривиальными зависимостям соединения -- Декомпозиция 6НФ невозможна без потерь -- Применяется во временных рядах -- -- id клиента -- Интервал дат -- Улица | -- Город | -- Штат | - Каждое из полей может изменяться независимоб что приводит к дублированию данных -- Индекс | -- Телефон | -- Можно разбить на отдельные отношения, но из-за того что интервалы могут не пересекаться сложно джойнить -- circular inclusion constraint требует при наличии хотя бы одной строки из (улицы, города, штата, индекса) -- все остальные тоже должны существовать. Это требование невозможно выполнить в современных БД, -- поэтому ограничения выносятся в слой приложения