Данный материал позволит вам подготовиться к собеседованию, освежить знания или познакомиться с такими терминами как транзакции, ACID и уровни изоляции.

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

Транзакция

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

Этот подход обеспечивает надежность данных, предотвращая ошибки и искажения. В случае сбоя во время транзакции база данных откатывает все изменения, чтобы сохранить целостность данных.

Пример успешной транзакции — банковский перевод между двумя людьми. Данная транзакция включает две операции: списание средств с одного баланса и зачисление на другой.

Пример транзакции
Пример транзакции

У транзакции есть два исхода — rollback и commit.

  • Rollback — откат совершенных изменений в базе данных. Rollback возвращает БД в состояние до транзакции.

  • Commit — фиксация всех изменений, сделанных в рамках транзакции, и сохранение их в базе данных.

Пример rollback и commit
Пример rollback и commit

ACID

Чтобы транзакция успешно выполнялась в базе данных, она должна обладать следующими свойствами:

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

  2. Consistency (Согласованность) — принцип, согласно которому база данных гарантирует, что после успешного завершения каждой транзакции, данные в ней остаются в согласованном состоянии. Другими словами, до и после выполнения транзакции данные остаются надёжными и достоверными. Например, в контексте банковского перевода это означает, что транзакция не приведёт к появлению отрицательного баланса на одном счете, и одновременно к положительному на другом. Таким образом, мы можем быть уверены в достоверности и корректности данных в базе.

  3. Isolation (Изолированность) — свойство, гарантирующее, что выполнение одной транзакции не влияет на выполнение других. Каждая транзакция должна работать независимо от других, даже если они выполняются параллельно. Это предотвращает конфликты и сохраняет целостность данных.

  4. Durability (Надёжность) — «что написано пером, не вырубишь топором». Это свойство гарантирует, что после успешного завершения транзакции изменения будут сохранены и доступны в БД. После того, как произошел commit транзакции, мы можем быть уверены, что изменения не будут отменены из-за какого-либо сбоя.

CAP теорема

Казалось бы, можно придерживаться свойств ACID и наслаждаться жизнью. Но жизнь не была бы такой сладкой без ограничений. Речь идет о CAP теореме, которая ограничивает нас в создании распределённых систем. Важно понимать, что теорема CAP и ACID не противоречат друг другу, а дополняют.

CAP теорема утверждает, что в распределенной системе невозможно одновременно обеспечить согласованность данных (Consistency), доступность (Availability) и устойчивость к разделению сети (Partition tolerance). Можно обеспечить только два из этих трёх свойств одновременно.

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

Например, банковские операции требуют согласованности и доступности, в то время как сервис отчетности — согласованности и устойчивости к разделению. С другой стороны, рекламное объявление букмекерской компании требует доступности и устойчивости к разделению, где согласованность не столь важна.

Untitled

Уровни изоляции

Взглянем поближе на понятие изоляции транзакций.

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

Разберём 3 основных явления, которые могут случиться при работе с транзакциями.

Грязное чтение (dirty read).

Возникает, когда одна транзакция читает данные, изменённые или вставленные другой транзакцией, которая ещё не была завершена и может быть откатана.

Суть в том, что транзакция видит изменения другой транзакции до их окончательного фиксирования, что может привести к принятию решений на основе неподтвержденных данных.

Грязное чтение
Грязное чтение

Неповторяющееся чтение (non repeatable read).

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

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

Основное различие от грязного чтения состоит в том, что:

  • в случае грязного чтения транзакция видит изменения другой транзакции до их окончательного фиксирования, даже если эта другая транзакция в конечном итоге откатится;

  • в неповторяющемся чтении учитываются лишь завершённые транзакции.

Неповторяющееся чтение
Неповторяющееся чтение

Фантомное чтение (phantom read).

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

Здесь ключевое — набор строк, которые нужны для одной транзакции: он стал больше, меньше или исчез из-за другой транзакции.

Фантомное чтение
Фантомное чтение

Существуют уровни изоляции транзакций, которые определяют, какие типы феноменов изоляции допускаются:

  • Read Uncommitted (Чтение незафиксированных данных): транзакция может читать данные, которые могут быть изменены другими транзакциями, но ещё не зафиксированы.

  • Read Committed (Чтение зафиксированных данных): транзакция читает только те данные, которые уже были зафиксированы другими транзакциями. Это предотвращает грязное чтение, но может допускать неповторяющееся чтение и фантомные чтения.

  • Repeatable Read (Повторяющееся чтение): транзакция читает данные, гарантируя, что прочитанные данные не будут изменены другими транзакциями до завершения самой транзакции. Это предотвращает неповторяющееся чтение, но может допускать фантомные чтения.

  • Serializable (Упорядочиваемость): самый строгий уровень изоляции, гарантирующий, что транзакции выполняются так, как если бы они выполнялись последовательно, без параллельного выполнения. Это предотвращает фантомные чтения, неповторяющееся чтение и грязное чтение, обеспечивая полную изоляцию транзакций.

Untitled

Перед нами стоит выбор: скорость или надежность.

Чем выше требуется надежность данных, тем больше ресурсов необходимо для поддержания этого уровня. Решение зависит от потребностей проекта и бизнеса. Некоторые приложения могут жертвовать изоляцией для увеличения скорости, но важно помнить, что для критически важных данных необходимо выбирать более строгие уровни изоляции. Анализ требований и рисков поможет сделать правильный выбор и обеспечить оптимальное сочетание скорости и надёжности данных для вашего приложения.

isolation1.drawio (2).png
Чем выше согласованность данных, тем ниже производительность системы

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

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


Рассказываю о процессах разработки, системном и бизнес-анализе, а также о карьере в IT в telegram.

Комментарии (10)


  1. AlexeyK77
    06.05.2024 08:12
    +5

    Вспоминая старые времена, когда передний край ИТ крутился вокруг СУБД и тонкостей реализации в них поддержки уровней изоляции, то хорошо бы в статье написать про обратную сторону медали - какими решениями и технологиями СУБД обеспечивает это все: Вспоминается варианты наложения блокировок: table level lock, page level lock, row level lock и их эскалация. Как они хранятся (в памяти, на диске)

    Типы блокировок: shared (READ|UPDATE), Intent, Exclusive и животворящий deadlock

    И в конце рассказать, почему в итоге гонку технологий выиграла snapshot isolation и все промышленные субд которые не поддерживали ее так или иначе проиграли (DB2, informix, sybase ase).

    Помню, какие были жаркие споры в начале 200х на sql.ru по поводу клссмических блокировчников и версионников и их работы на ресурсах серверов того времени. Ностальгия.


  1. Akina
    06.05.2024 08:12

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

    Dirty Read - чтение незафиксированных изменений значений.

    Non-repeatable Read - чтение зафиксированных изменений значений.

    Phantom Read - чтение зафиксированных изменений набора.

    Чтение незафиксированных изменений набора - ???

    Вы явно пишете:

    • в неповторяющемся чтении учитываются лишь завершённые транзакции.

    Соответственно вопрос - а что, чтение незафиксированных изменений набора вообще невозможно? никогда и ни при каких обстоятельствах? Уж коли оно вообще нигде не "посчитано"..


    1. AlexeyK77
      06.05.2024 08:12
      +3

      Non-repeatable Read это когда вы читаете одну и туже строку два раза подряд, но из-за конкурирующей транзакции, которая меняет значение в этой строке, второе чтение может вернуть значения в строке, отличные от первого чтения.

      Phantom Read - это когда вы читаете набор из нескольких строк опять таки два раза, но из-за конкурирующей транзакции, которая может удалить или изменить одну из строк так, что она уже не попадает под условия выборки, то после второго чтения результирующий набор будет отличаться от первого.

      В обеих случаях читаются только закомичченные данные, но влияние на результат отличается.

      Вот тут детально: https://stackoverflow.com/questions/11043712/non-repeatable-read-vs-phantom-read

      "Non-repeatable reads are when your transaction reads committed UPDATES from another transaction. The same row now has different values than it did when your transaction began.

      Phantom reads are similar but when reading from committed INSERTS and/or DELETES from another transaction. There are new rows or rows that have disappeared since you began the transaction."


      1. Akina
        06.05.2024 08:12

        Не надо мне это объяснять, я в курсе. Я указываю на погрешность статьи и вопрос, который легко может возникнуть у читателя и ответ на который в статье отсутствует. Вставленные, но не зафиксированные записи МОГУТ быть получены в параллельной транзакции. И вы должны это явно указать.

        Если вы только пользуетесь чужими текстами и пока не знаете, когда это возможно - ну поэкспериментируйте, в конце-то концов.


  1. mishamota
    06.05.2024 08:12
    +3

    Consistency (Согласованность) — принцип, согласно которому база данных гарантирует, что после успешного завершения каждой транзакции, данные в ней остаются в согласованном состоянии. Другими словами, до и после выполнения транзакции данные остаются надёжными и достоверными. Например, в контексте банковского перевода это означает, что транзакция не приведёт к появлению отрицательного баланса на одном счете, и одновременно к положительному на другом. Таким образом, мы можем быть уверены в достоверности и корректности данных в базе.

    А что мне мешает в одной (например pg) транзакции сделать две операции, которые загонят по одному счёту плюс, а по другому минус? Я нарушу принципы ACID?

    Такой ответ часто приходится слышать, но это скорее демонстрация непонимания, что есть на самом деле неконсистентность ACID.

    Если совсем простыми словами, то Consistency (ACID) обеспечивает гарантию, что в базе не будут нарушены ограничения (constraints). Например, не сможете затолкать в одну таблицу две записи с одним значением первичного ключа.


    1. ManGegenMann
      06.05.2024 08:12
      +1

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


      1. sergeykonshin
        06.05.2024 08:12
        +2

        По мне так вся эта статья набор баззвордов. Лучше бы про RowVersion(timestamp) и инвалидацию кеша в ORM написали


  1. silentz
    06.05.2024 08:12
    +1

    Ну ладно согласованность неправильно описали. Но кап теорема вообще не имеет отношения к бд, как таковой. Если у вас бд находится на одной машине - то она всегда удовлетворяет кап теореме. А вот если у вас несколько инстансов чего угодно (кеша, бд, вычислительных мощностей) разнесены сетью - тогда вступает в действие кап теорема. Она всегда о разделении сетью. А без P - CA (в рамках CAP) не имеют смысла


  1. TldrWiki
    06.05.2024 08:12

    Вот только это ушло в прошлое с появлением mvcc


  1. CentariumV
    06.05.2024 08:12

    Плохо, что нет конкретных примеров. А без примеров это все болтология - на собесах тоже часто спрашивают про уровни изоляции. Но главное, это когда до примеров доходит. Объяснить не могут, в каких случаях нужно какой уровень применять, при какой бизнес - логике. Теория это одно, практика - совсем другое

    И по поводу «снижения производительности». Тоже примеров нет - когда, на сколько? Есть специализированные benchmark -инструменты, вроде pgbench, есть универсальные, вроде HammerDB. Конкретика и примеры - вот что было бы интересно почитать