Что такое транзакция

    Транзакция — это набор операций по работе с базой данных (БД), объединенных в одну атомарную пачку.

    (Предполагается, что вы знаете, что такое БД. Но чуть позже тут будет ссылка на статью «что это такое»)

    Транзакционные базы данных (базы, работающие через транзакции) выполняют требования ACID, которые обеспечивают безопасность данных. В том числе финансовых данных =) Поэтому разработчики их и выбирают.

    Я расскажу о том, что такое транзакция. Как ее открыть, и как закрыть. И почему это важно — закрывать транзакцию. И тогда при написании запросов к базе у вас будет осознанное понимание, что происходит там, под капотом, и зачем же нужен этот обязательный коммит после апдейта.

    Содержание

    Что такое транзакция

    Транзакция — это архив для запросов к базе. Он защищает ваши данные благодаря принципу «всё, или ничего».

    Представьте, что вы решили послать другу 10 файликов в мессенджере. Какие есть варианты:

    1. Кинуть каждый файлик отдельно.

    2. Сложить их в архив и отправить архив.

    Вроде бы разницы особой нет. Но что, если что-то пойдет не так? Соединение оборвется на середине, сервер уйдет в ребут или просто выдаст ошибку...

    В первом случае ваш друг получит 9 файлов, но не получит один.

    Во втором не получит ничего. Нет промежуточных состояний. Или получил всё, или не получил ничего. Но зато если произошла ошибка, вы снова перешлете сообщение. И друг получит все файлики разом, не придется проверять «не потерялся ли кто».

    Казалось бы, ну недополучил файлик, что с того? А если это критично? Если это важные файлики? Например, для бухгалтерии. Потерял один файлик? Значит, допустил ошибку в отчете для налоговой. Значит, огребешь штраф и большие проблемы! Нет, спасибо, лучше файлы не терять!

    И получается, что тебе надо уточнять у отправителя:

    — Ты мне сколько файлов посылал?

    — 10

    — Да? У меня только 9... Давай искать, какой продолбался.

    И сидите, сравниваете по названиям. А если файликов 100 и потеряно 2 штуки? А названия у них вовсе не «Отчет 1», «Отчет 2» и так далее, а «hfdslafebx63542437457822nfhgeopjgrev0000444666589.xml» и подобные... Уж лучше использовать архив! Тогда ты или точно всё получил, или не получил ничего и делаешь повторную попытку отправки.

    Так вот! Транзакция — это тот же архив для запросов. Принцип «всё, или ничего». Или выполнены все запросы, которые разработчик упаковал в одну транзакцию, или ни один.

    Допустим, вы переводите все деньги с одной карточки на другую. Выглядит это "внутри" системы как несколько операций:

    delete from счет1 where счет = счет 1

    insert into счет2 values ('сумма')

    Принцип «всё или ничего» тут очень помогает. Было бы обидно, если бы деньги со счета1 списались, но на счет2 не поступили... Потому что соединение оборвалось или вы в номере счета опечатались и система выдала ошибку...

    Но благодаря объединению запросов в транзакцию при возникновении ошибки зачисления мы откатываем и операцию списания. Деньги снова вернулись на счет 1!

    Если говорить по-научному, то транзакция — упорядоченное множество операций, переводящих базу данных из одного согласованного состояния в другое. Согласованное состояние — это состояние, которое подходит под бизнес-логику системы. То есть у нас не остается отрицательный баланс после перевода денег, номер счета не «зависает в воздухе», не привязанный к человеку, и тому подобное.

     

    Как отправить транзакцию

    Чтобы обратиться к базе данных, сначала надо открыть соединение с ней. Это называется коннект (от англ. connection, соединение). Коннект — это просто труба, по которой мы посылаем запросы.

    Чтобы сгруппировать запросы в одну атомарную пачку, используем транзакцию. Транзакцию надо:

    1. Открыть.

    2. Выполнить все операции внутри.

    3. Закрыть.

    Как только мы закрыли транзакцию, труба освободилась. И ее можно переиспользовать, отправив следующую транзакцию.

    Можно, конечно, каждый раз закрывать соединение с БД. И на каждое действие открывать новое. Но эффективнее переиспользовать текущие. Потому что создание нового коннекта — тяжелая операция, долгая.

    При настройке приложения администратор указывает, сколько максимально открытых соединений с базой может быть в один момент времени. Это называется пул соединений — количество свободных труб.

    Разработчик берет соединение из пула и отправляет по нему транзакцию. Как только транзакция закрывается (неважно, успешно она прошла или откатилась), соединение возвращается в пул, и его может использовать следующая бизнес-операция.

     

    Как открыть транзакцию

    Зависит от базы данных. В Oracle транзакция открывается сама, по факту первой изменяющей операции. А в MySql надо явно писать «start transaction».

     

    Как закрыть транзакцию

    Тут есть 2 варианта:

    1. COMMIT — подтверждаем все внесенные изменения;

    2. ROLLBACK — откатываем их;

    И вся фишка транзакционной базы в том, что база сначала применяет запрос «виртуально», реально ничего в базе не изменив. Ты можешь посмотреть, как запрос изменит базу, ничего при этом не сохраняя.

    Например, я пишу запрос:

    insert into clients (name, surname) values ('Иван', 'Иванов');
    -- добавь в таблицу клиентов запись с именем «Иван» и фамилиев «Иванов»

    Запрос выполнен успешно, хорошо! Теперь, если я сделаю select из этой таблицы, прям тут же, под своим запросом — он находит Иванова! Я могу увидеть результат своего запроса.

    Но! Если открыть графический интерфейс программы, никакого Иванова мы там не найдем. И даже если мы откроем новую вкладку в sql developer (или в другой программе, через которую вы подключаетесь к базе) и повторим там свой select — Иванова не будет.

    А все потому, что я не сделала коммит, не применила изменения:

    insert into clients (name, surname) values ('Иван', 'Иванов');
    commit;

    Я могу добавить кучу данных. Удалить полтаблицы. Изменить миллион строк. Но если я закрою вкладку sql developer, не сделав коммит, все эти изменения потеряются.

    Когда я впервые столкнулась с базой на работе, я часто допускала такую ошибку: подправлю данные «на лету» для проведения теста, а в системе ничего не меняется! Почему? Потому что коммит сделать забыла.

    На самом деле это удобно. Ведь если ты выполняешь сложную операцию, можно посмотреть на результат. Например, удаляем тестовые данные. Написали кучу условий из серии:

    Если имя = «Тест»

    И фамилия = «Тестовый»

    ...

    Удалили. Делаем select count — посмотреть количество записей в таблице. А там вместо миллиона строк осталось 100 тысяч! Если база реальная, то это очень подозрительно. Врядли там было СТОЛЬКО тестовых записей.

    Проверяем свой запрос, а мы там где-то ошиблись! Вместо «И» написали «ИЛИ», или как-то еще. Упс... Хорошо еще изменения применить не успели. Вместо коммита делаем rollback.

    Тут может возникнуть вопрос — а зачем вообще нужен ROLLBACK? Ведь без коммита ничего не сохранится. Можно просто не делать его, и всё. Но тогда транзакция будет висеть в непонятном статусе. Потому что ее просто так никто кроме тебя не откатит.

    Или другой вариант. Нафигачили изменений:

    Удалить все строки, где имя «Иван»;

    Поменять код города с 495 на 499;

    ....

    Но видим, что операцию надо отменять. Проверочный select заметил, что база стала неконсистентной. А мы решили «Ай, да ладно, коммит то не сделали? Значит, оно и не сохранится». И вернули соединение в пул.

    Следующая операция бизнес-логики берет это самое соединение и продолжает в нем работать. А потом делает коммит. Этот коммит относился к тем 3 операциям, что были внутри текущей транзакции. Но мы закоммитили еще и 10 других — тех, что в прошлый раз откатить поленились. Тех, которые делают базу неконсистентной...

    Так что лучше сразу сделайте откат. Здоровей система будет!

    Итого

    Транзакция — набор операций по работе с базой данных, объединенных в одну атомарную пачку.

    Одной операции всегда соответствует одна транзакция, но в рамках одной транзакции можно совершить несколько операций (например, несколько разных insert можно сделать, или изменить и удалить данные...).

    Чтобы отправить транзакцию к базе, нам нужно создать соединение с ней. Или переиспользовать уже существующее. Соединение называют также коннект (англ connection) — это просто труба, по которой отправляются запросы. У базы есть пул соединений — место, откуда можно взять любое и использовать, они там все свободные.

    В некоторых системах транзакцию нужно открыть, в других она открывается сама. А вот закрыть ее нужно самостоятельно. Варианты:

    1. COMMIT — подтверждаем все внесенные изменения;

    2. ROLLBACK — откатываем их;

    Делая комит, мы заканчиваем одну бизнес-операцию, и возвращаем коннект в пул без открытой транзакции. То есть просто освобождаем трубу для других. Следующая бизнес-операция берет эту трубу и фигачит в нее свои операции. Поэтому важно сделать rollback, если изменения сохранять не надо. Не откатите и вернете соединение в пул? Его возьмет кто-то другой и сделает коммит. Своих изменений, и ваших, неоткаченных.

    Не путайте соединение с базой (коннект) и саму транзакцию. Коннект — это просто труба, операции (update, delete…) мы посылаем по трубе, старт транзакции и commit /rollback — это группировка операций в одну атомарную пачку.

    См также:

    Блокировки транзакций — что может пойти не так при одновременном редактировании

    PS — больше полезных статей ищите в моем блоге по метке «полезное». А полезные видео — на моем youtube-канале

    Комментарии 30

      +6
      Хм, простите, но как по мне это статья из разряда «Хозяйкам на заметку» :)
      У базы есть пул соединений

      Да ладно! Подозреваю, что речь таки про какой-то сервер приложений…
      А там вместо миллиона строк осталось 100 тысяч! Если база реальная, то это очень подозрительно

      Если база реальная и Вы выполнили подобную операцию, заблокировав все задетые строки и НЕ сделали сразу commit или rollback, то Ваше подозрение будет наименьшей Вашей проблемой — так как все эти строки заблокированы и прилетит скорее всего оттуда, откуда не ждали :)
        +1
        Да, это статья именно из этого разряда! :)
        +4
        А главное — пока транзакция не закрыта, нужно рассказать про блокировки.
          0
          Ага, точно! Дала ссылку в «см также» блоке, спасибо))
          +2
          А еще нужно рассказать про чтение фантомных данных
          и вот эти все уровни изоляции транзакций
            +2
            Ну а не проще просто взять и прочитать википедию? Там ведь все это написано, на русском языке, и более подробно? Зачем такие пересказы для детского сада с картинками? Для кого?
              +5

              Мне лично нравится такая подача, про транзакции читать унылый википедийный текст никто без необходимости не будет.

                0
                Не, я не имею ничего против просветительской деятельности. Если есть какое-то число людей, которым такой уровень подачи хорош, то и пусть себе.

                Мне просто как-то сомнительно что именно тут, в ИТ-сообществе, найдется много людей, которые все еще не знают, что такое транзакции.
                  +1

                  Вы, наверное, удивитесь, узнав, как много школьников сейчас в айти-сообществе, знающих только JS/Python.

                    0
                    Не, не удивлюсь. Мне просто кажется, дело немного не в школьниках. Если вы школьник, и хотите понять, что такое базы данных, транзакции, и т.п., т.е. серьезно этим заниматься, и сделать это своей профессией в конечном счете — то на мой взгляд, лучше читать нормальные серьезные книги. А не такие вводные тексты, которые может и интереснее, но тут реально опущены ряд важнейших тем, типа тех же уровней изоляции (уже тут упомянули).

                    Ну т.е. школьники, которые хотят в ИТ как в профессию — все равно странная аудитория для таких текстов (опять же — ничего не имею против такого формата подачи как такового). Но я давно не школьник, так что это лишь чисто ссубъективное мнение человека, который еще самую первую SQL базу видел живьем :)
                      0
                      ИМХО такие книги часто сложно написаны и если ты читаешь такое в первый раз, подобные статьи в которых это рассказывается как ребенку могут помочь
                      лучше читать нормальные серьезные книги

                      лично я предпочитаю такой же подход в обучение, либо нормальные книги, либо сразу в исходники. Но для новичка это может быть достаточно сложно
                      П.с я сам начинал программировать лет в 16 и тогда такого большого разнообразия материалов для обучения не было и я все еще помню как было больно учиться по тем книгам
                        +1
                        Ну, когда я начинал программировать, книги издавались в бумажном виде. И чтобы купить что-то приличное, приходилось записываться в начале года, чтобы тебе прислали открытку о выходе той или иной книги.

                        >Но для новичка это может быть достаточно сложно
                        Несомненно. Но я все же думаю, что учиться по комиксам это перебор :)
                          +1
                          >Но я все же думаю, что учиться по комиксам это перебор :)

                          Первая и лучшая книга в мой жизни была комиксом по сути и называлась
                          «Энциклопедия профессора Фортрана». Скажу точно, она не была перебором.
                            0
                            Вы знаете, тут уже очень давно публиковали статью на тему монад, функторов и т.п. в картинках. Типичный комикс, в общем-то. Она, на мой взгляд, более чем достойная. Вопрос скорее в том, что учиться только по таким все-таки не стоит — лучше все равно почитать что-то более фундаментальное. Но как стимул изучить что-то — комиксы тоже годятся.

                            P.S. Книга про которую вы говорите, вышла кажется в 1991. Я к этому времени руководил группой системных программистов большого ВЦ, так что мы видимо из разных поколений. Но разные взгляды — это вполне нормально, так что я тут не спорю, если что, а просто изгалаю свой взгляд на вещи.
                        +1

                        Мне кажется, тут проблема в некотором искажении информации (выше уже говорили про пул, что он не в базе заводится, точнее — бывает пулинг рядом с бд, чтобы бороться с приложениями, не умеющими в свой пулинг, но для приложения такие пулы совершенно прозрачный и незакрытую транзакцию должны обрабатывать через rollback).


                        Для нормального просветительского уровня надо убрать инфу про пулы (либо расписать отдельно) и убрать про вкладки sql developer-а — чересчур специфичный опыт для начально-ознакомительного уровня.

                          0
                          Так я в общем примерно об этом с начала и писал — что не очень понятно, для кого такой текст? Он содержит ряд упрощений и искажений (вероятно, с той же целью упростить), который делает текст плохим для начального ознакомления с предметом. Упрощать нужно — но упростить так, чтобы это осталось правильным, это сложно.
                          0
                          Обожаю книги из серии Head First O`Reilly, хотя там тоже такая же «несерьезная» подача материала. Каждому своё )
                            0
                            Да я могу и в третий раз сказать — ничего не имею против такого формата. Он просто сложный — важно ничего не упустить, а то создается искаженное понимание (при этом нюансов в этой тебе вообще вагон). В этом случае на мой взгляд немного не получилось, но бывает.
                              0
                              Возможно, просто у меня, как у автора, была идея донести конкретные мысли — как открыть, как закрыть, потому что сама лично с этим сталкивалась. Понятно, что еще много всякого интересного можно нагуглить про транзакции, если дадите хорошие ссылки, добавлю в статью)
                                0
                                Ссылки — вряд ли. Есть одна тема, достаточно сложная — у Oracle есть такая штука, как www.orafaq.com/wiki/SCN, системный номер изменения (ну или коммита). И там можно просто в рамках select запросить данные в том виде, какими они были после конкретной транзакции, указав этот конкретный SCN (с кучей нюансов). Но вряд ли это нужно той аудитории, которой могут зайти статьи в подобном формате.
                  0
                  Да да, я уже на статью с блокировками дала ссылку, спасибо)
                  0
                  Программирование — детям! Не осуждаю, у самого в детстве была книга японских авторов с картинками, логика работы ЭВМ прививается решительно
                    0
                    Если строки уже удалены — то и значит, что транзакция подтверждена. А пока она не подтверждена — об этом знает только тот, кто их удалил (фантомные чтения не рассматриваем).
                    Так что КДПВ несколько неточная.
                      0
                      +1 за форму подачи. Художник — сам автор, или кто-то другой? Где можно посмотреть другие работы?
                        +3
                        Художник — это кто-то другой ) Раньше у меня была одна художница, сейчас уже около 7, чтобы быстрее книгу закончить)) Другие работы художников можно посмотреть тут testbase.ru/book-beginner
                        0
                        Интересная форма подачи информации! Автору- респект!
                          0
                          Спасибо ^_^
                          +1
                          Картинки классные
                            0
                            Лет 20 назад, когда интернет был через dial-up с оплатой скретч карами, у моего провайдера несколько раз удавалось пополнить баланс одним кодом более одного раза. Сейчас конечно за это стыдно, но что было то было.
                            Т.е. в 2-3 браузерах открывалась форма оплаты и максимально быстро отправлялся один и тот же код.
                            Суть в отсутствии все тех же транзакций и медленных серверах (да и вообще железе в целом) в то время. Их скрипт видимо сперва проверял правильность кода, был ли он ранее использован, потом добавлял сумму на баланс и в конце добавлял код в базу использованных. Так вот до последнего этапа можно было вполне реально вклиниться еще как минимум раз с тем же самым кодом.
                            Вот один из реальных примеров как отсутствие транзакций может навредить.
                              0
                              Я так понимаю в данном случае помочь?

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое