فهرست منبع

дописал про транзакции

Евгений Колесников 5 ماه پیش
والد
کامیت
a262bfecdd
2فایلهای تغییر یافته به همراه159 افزوده شده و 3 حذف شده
  1. 155 1
      articles/sql_trigger.md
  2. 4 2
      readme.md

+ 155 - 1
articles/sql_trigger.md

@@ -3,7 +3,7 @@
 [Представления (View)](./sql_view.md) | [Содержание](../readme.md#проектирование-баз-данных) | [Создание подключения к БД MySQL. Получение данных с сервера.](./cs_mysql_connection3.md)
 
 
-# Хранимые процедуры. Триггеры.
+# Хранимые процедуры. Триггеры. Транзакции.
 
 ## Хранимые процедуры
 
@@ -125,6 +125,160 @@ end;
 $$
 ```
 
+## Транзакции
+
+>Содрано [с хабра](https://habr.com/ru/articles/537594/)
+
+Транзакция — это набор операций по работе с базой данных (БД), объединенных в одну атомарную пачку.
+
+Транзакционные базы данных (базы, работающие через транзакции) выполняют требования ACID, которые обеспечивают безопасность данных. В том числе финансовых данных =) Поэтому разработчики их и выбирают.
+
+Я расскажу о том, что такое транзакция. Как ее открыть, и как закрыть. И почему это важно — закрывать транзакцию. И тогда при написании запросов к базе у вас будет осознанное понимание, что происходит там, под капотом, и зачем же нужен этот обязательный коммит после апдейта.
+
+### Что такое транзакция
+
+Транзакция — это архив для запросов к базе. Он защищает ваши данные благодаря принципу «всё, или ничего».
+
+Представьте, что вы решили послать другу 10 файликов в мессенджере. Какие есть варианты:
+
+1. Кинуть каждый файлик отдельно.
+1. Сложить их в архив и отправить архив.
+
+Вроде бы разницы особой нет. Но что, если что-то пойдет не так? Соединение оборвется на середине, сервер уйдет в ребут или просто выдаст ошибку...
+
+В первом случае ваш друг получит `9` файлов, но не получит один.
+
+Во втором не получит ничего. Нет промежуточных состояний. Или получил всё, или не получил ничего. Но зато если произошла ошибка, вы снова перешлете сообщение. И друг получит все файлики разом, не придется проверять «не потерялся ли кто».
+
+>Казалось бы, ну недополучил файлик, что с того? А если это критично? Если это важные файлики? Например, для бухгалтерии. Потерял один файлик? Значит, допустил ошибку в отчете для налоговой. Значит, огребешь штраф и большие проблемы! Нет, спасибо, лучше файлы не терять!
+>
+>И получается, что тебе надо уточнять у отправителя:
+>
+>— Ты мне сколько файлов посылал?
+>
+>— 10
+>
+>— Да? У меня только 9... Давай искать, какой потерялся.
+>
+>И сидите, сравниваете по названиям. А если файликов 100 и потеряно 2 штуки? А названия у них вовсе не «Отчет 1», «Отчет 2» и так далее, а «hfdslafebx63542437457822nfhgeopjgrev0000444666589.xml» и подобные... Уж лучше использовать архив! Тогда ты или точно всё получил, или не получил ничего и делаешь повторную попытку отправки.
+
+Так вот! Транзакция — это тот же архив для запросов. Принцип «всё, или ничего». Или выполнены все запросы, которые разработчик упаковал в одну транзакцию, или ни один.
+
+>Допустим, вы переводите все деньги с одной карточки на другую. Выглядит это "внутри" системы как несколько операций:
+>
+>```sql
+>delete from счет1 where счет = счет 1
+>
+>insert into счет2 values ('сумма')
+>```
+>
+>Принцип «всё или ничего» тут очень помогает. Было бы обидно, если бы деньги со счета1 списались, но на счет2 не поступили... Потому что соединение оборвалось или вы в номере счета опечатались и система выдала ошибку...
+>
+>Но благодаря объединению запросов в транзакцию при возникновении ошибки зачисления мы откатываем и операцию списания. Деньги снова вернулись на счет 1!
+
+Если говорить по-научному, то транзакция — упорядоченное множество операций, переводящих базу данных из одного согласованного состояния в другое. Согласованное состояние — это состояние, которое подходит под бизнес-логику системы. То есть у нас не остается отрицательный баланс после перевода денег, номер счета не «зависает в воздухе», не привязанный к человеку, и тому подобное.
+
+### Как отправить транзакцию
+
+Чтобы обратиться к базе данных, сначала надо открыть соединение с ней. Это называется коннект (от англ. connection, соединение). Коннект — это просто труба, по которой мы посылаем запросы.
+
+Чтобы сгруппировать запросы в одну атомарную пачку, используем транзакцию. Транзакцию надо:
+
+1. Открыть.
+1. Выполнить все операции внутри.
+1. Закрыть.
+
+Как только мы закрыли транзакцию, труба освободилась. И ее можно переиспользовать, отправив следующую транзакцию.
+
+Можно, конечно, каждый раз закрывать соединение с БД. И на каждое действие открывать новое. Но эффективнее переиспользовать текущие. Потому что создание нового коннекта — тяжелая операция, долгая.
+
+При настройке приложения администратор указывает, сколько максимально открытых соединений с базой может быть в один момент времени. Это называется пул соединений — количество свободных труб.
+
+Разработчик берет соединение из пула и отправляет по нему транзакцию. Как только транзакция закрывается (неважно, успешно она прошла или откатилась), соединение возвращается в пул, и его может использовать следующая бизнес-операция.
+
+### Как открыть транзакцию
+
+Зависит от базы данных. В Oracle транзакция открывается сама, по факту первой изменяющей операции. А в MySql надо явно писать «start transaction».
+ 
+### Как закрыть транзакцию
+
+Тут есть 2 варианта:
+
+* COMMIT — подтверждаем все внесенные изменения;
+* ROLLBACK — откатываем их;
+
+И вся фишка транзакционной базы в том, что база сначала применяет запрос «виртуально», реально ничего в базе не изменив. Ты можешь посмотреть, как запрос изменит базу, ничего при этом не сохраняя.
+
+Например, я пишу запрос:
+
+```sql
+insert into clients (name, surname) 
+values ('Иван', 'Иванов');
+-- добавь в таблицу клиентов запись с именем «Иван» и фамилиев «Иванов»
+```
+
+Запрос выполнен успешно, хорошо! Теперь, если я сделаю select из этой таблицы, прям тут же, под своим запросом — он находит Иванова! Я могу увидеть результат своего запроса.
+
+Но! Если открыть графический интерфейс программы, никакого Иванова мы там не найдем. И даже если мы откроем новую вкладку в sql developer (или в другой программе, через которую вы подключаетесь к базе) и повторим там свой select — Иванова не будет.
+
+А все потому, что я не сделала коммит, не применил изменения:
+
+```sql
+insert into clients (name, surname) 
+values ('Иван', 'Иванов');
+
+commit;
+```
+
+Я могу добавить кучу данных. Удалить полтаблицы. Изменить миллион строк. Но если я закрою вкладку sql developer, не сделав коммит, все эти изменения потеряются.
+
+На самом деле это удобно. Ведь если ты выполняешь сложную операцию, можно посмотреть на результат. Например, удаляем тестовые данные. Написали кучу условий из серии:
+
+>Если имя = «Тест»
+>
+>И фамилия = «Тестовый»
+>
+>...
+
+Удалили. Делаем select count — посмотреть количество записей в таблице. А там вместо миллиона строк осталось 100 тысяч! Если база реальная, то это очень подозрительно. Врядли там было СТОЛЬКО тестовых записей.
+
+Проверяем свой запрос, а мы там где-то ошиблись! Вместо «И» написали «ИЛИ», или как-то еще. Упс... Хорошо еще изменения применить не успели. Вместо коммита делаем rollback.
+
+Тут может возникнуть вопрос — а зачем вообще нужен ROLLBACK? Ведь без коммита ничего не сохранится. Можно просто не делать его, и всё. Но тогда транзакция будет висеть в непонятном статусе. Потому что ее просто так никто кроме тебя не откатит.
+
+Или другой вариант. Нафигачили изменений:
+
+```
+Удалить все строки, где имя «Иван»;
+
+Поменять код города с 495 на 499;
+
+...
+```
+
+Но видим, что операцию надо отменять. Проверочный select заметил, что база стала неконсистентной. А мы решили «Ай, да ладно, коммит то не сделали? Значит, оно и не сохранится». И вернули соединение в пул.
+
+Следующая операция бизнес-логики берет это самое соединение и продолжает в нем работать. А потом делает коммит. Этот коммит относился к тем 3 операциям, что были внутри текущей транзакции. Но мы закоммитили еще и 10 других — тех, что в прошлый раз откатить поленились. Тех, которые делают базу неконсистентной...
+
+Так что лучше сразу сделайте откат. Здоровей система будет!
+
+### Итого
+
+Транзакция — набор операций по работе с базой данных, объединенных в одну атомарную пачку.
+
+Одной операции всегда соответствует одна транзакция, но в рамках одной транзакции можно совершить несколько операций (например, несколько разных insert можно сделать, или изменить и удалить данные...).
+
+Чтобы отправить транзакцию к базе, нам нужно создать соединение с ней. Или переиспользовать уже существующее. Соединение называют также коннект (англ connection) — это просто труба, по которой отправляются запросы. У базы есть пул соединений — место, откуда можно взять любое и использовать, они там все свободные.
+
+В некоторых системах транзакцию нужно открыть, в других она открывается сама. А вот закрыть ее нужно самостоятельно. Варианты:
+
+* COMMIT — подтверждаем все внесенные изменения;
+* ROLLBACK — откатываем их;
+
+Делая комит, мы заканчиваем одну бизнес-операцию, и возвращаем коннект в пул без открытой транзакции. То есть просто освобождаем трубу для других. Следующая бизнес-операция берет эту трубу и фигачит в нее свои операции. Поэтому важно сделать rollback, если изменения сохранять не надо. Не откатите и вернете соединение в пул? Его возьмет кто-то другой и сделает коммит. Своих изменений, и ваших, неоткаченных.
+
+Не путайте соединение с базой (коннект) и саму транзакцию. Коннект — это просто труба, операции (update, delete…) мы посылаем по трубе, старт транзакции и commit / rollback — это группировка операций в одну атомарную пачку. 
+
 Предыдущая лекция |   | Следующая лекция
 :----------------:|:----------:|:----------------:
 [Представления (View)](./sql_view.md) | [Содержание](../readme.md#проектирование-баз-данных) | [Создание подключения к БД MySQL. Получение данных с сервера.](./cs_mysql_connection3.md)

+ 4 - 2
readme.md

@@ -201,13 +201,15 @@ http://sergeyteplyakov.blogspot.com/2014/01/microsoft-fakes-state-verification.h
 1. [Создание ER-диаграммы](./articles/5_1_1_1_erd_workbench.md)
 1. [Создание базы данных. Импорт данных.](./articles/sql_import.md)
 1. [Представления (View)](./articles/sql_view.md)
-1. [Хранимые процедуры. Триггеры.](./articles/sql_trigger.md)
+1. [Хранимые процедуры. Триггеры. Транзакции.](./articles/sql_trigger.md)
 
 <!-- 
 https://office-menu.ru/uroki-sql Уроки SQL
 
 В "основы" добавить 
-- count и функции работы со временем (between)  
+- count и функции работы со временем (between) 
+- транзакции
+- какая команда возвращает количество измененных строк (для триггера) 
 -->
 
 ---