Введение

В базах данных и обработке транзакций (управлении транзакциями) изоляция моментального снимка – это гарантия того, что все чтения, выполненные в рамках транзакции, будут видеть согласованный снимок базы данных (на практике – последние зафиксированные значения, существовавшие на момент её начала), а сама транзакция успешно завершится только в том случае, если её изменения не конфликтуют ни с какими параллельными изменениями, сделанными после этого снимка. Изоляция моментального снимка реализована в нескольких крупных системах управления базами данных, таких как InterBase, Firebird, Oracle, MySQL, PostgreSQL, SQL Anywhere, MongoDB и Microsoft SQL Server (версии 2005 и более поздние). Основная причина её широкого распространения заключается в том, что она обеспечивает более высокую производительность по сравнению с сериализуемостью, при этом избегая большинства аномалий параллельного доступа, которые избегает сериализуемость (но не всех). На практике изоляция моментального снимка реализуется в рамках многоверсионного управления конкурентным доступом (MVCC), где поддерживаются поколения значений каждого элемента данных (версии): MVCC – распространенный способ повышения параллельности и производительности за счет создания новой версии объекта базы данных при каждой его записи и предоставления транзакциям возможности читать несколько последних релевантных версий (каждого объекта). Изоляция моментального снимка использовалась для критики определения уровней изоляции в стандарте ANSI SQL 92, поскольку она не проявляет ни одной из "аномалий", запрещенных этим стандартом SQL, но при этом не является сериализуемой (уровень изоляции, свободный от аномалий, определенный ANSI). Несмотря на отличие от сериализуемости, Oracle иногда называет изоляцию моментального снимка сериализуемой.

Определение

Транзакция, выполняемая в режиме изоляции моментальных снимков, работает как будто с персональным снимком базы данных, сделанным в начале транзакции. Транзакция успешно завершится только если значения, обновленные транзакцией, не были изменены извне после создания снимка. Такой конфликт записи-записи приведет к откату транзакции. В аномалии искажения записи (write skew) две транзакции (T1 и T2) одновременно читают пересекающиеся наборы данных (например, значения V1 и V2), выполняют непересекающиеся обновления (например, T1 обновляет V1, T2 обновляет V2) и затем одновременно фиксируют изменения, ни одна из них не видя обновление, выполненное другой. Если бы система была сериализуемой, такая аномалия была бы невозможна, поскольку либо T1, либо T2 должны были бы выполниться первыми и быть видимыми для другой транзакции. В отличие от этого, изоляция моментальных снимков допускает аномалии искажения записи. В качестве конкретного примера, представим, что V1 и V2 – это два баланса, принадлежащие одному человеку, Филу. Банк разрешает дефицит на любом из балансов (V1 или V2), при условии, что общая сумма на обоих счетах никогда не будет отрицательной (то есть V1 + V2 ≥ 0). В настоящее время оба баланса составляют $100. Фил одновременно инициирует две транзакции: T1 снимает $200 с V1, а T2 снимает $200 с V2. Если база данных гарантирует сериализуемые транзакции, самый простой способ реализации T1 – вычесть $200 из V1, а затем проверить, что V1 + V2 ≥ 0 по-прежнему выполняется, и откатить транзакцию в противном случае. T2 аналогично вычитает $200 из V2 и затем проверяет V1 + V2 ≥ 0. Поскольку транзакции должны сериализоваться, либо T1 выполнится первой, оставив V1 = -$100, V2 = $100, и предотвратив успешное завершение T2 (поскольку V1 + (V2 - $200) теперь равно -$200), либо T2 выполнится первой и аналогично предотвратит фиксацию T1. Однако, если база данных использует изоляцию моментальных снимков (MVCC), T1 и T2 работают с приватными снимками базы данных: каждая транзакция вычитает $200 со своего счета и затем проверяет, что новая общая сумма равна нулю, используя значение другого счета, которое было актуально на момент создания снимка. Поскольку ни одно из обновлений не вызывает конфликтов, обе транзакции успешно фиксируются, оставляя V1 = V2 = -$100, а V1 + V2 = -$200. Некоторые системы, построенные с использованием многоверсионного контроля параллелизма (MVCC), могут поддерживать (только) изоляцию моментальных снимков, чтобы транзакции могли выполняться, не беспокоясь о параллельных операциях и, что более важно, без необходимости повторной проверки всех операций чтения при фиксации транзакции. Это удобно, поскольку MVCC поддерживает последовательность состояний, соответствующих различным моментам времени. Единственная информация, которую необходимо хранить во время транзакции, – это список выполненных обновлений, которые можно относительно легко просканировать на наличие конфликтов перед фиксацией. Однако системы MVCC (такие как MarkLogic) используют блокировки для сериализации операций записи вместе с MVCC, чтобы получить некоторые преимущества в производительности и при этом поддерживать более высокий уровень изоляции – "сериализуемость".

Обходные пути

Потенциальные проблемы несогласованности, возникающие из-за аномалий при записи, могут быть устранены путем добавления (в противном случае ненужных) обновлений к транзакциям для обеспечения свойства сериализуемости. Материализация конфликта: добавьте специальную таблицу конфликтов, которую обе транзакции будут обновлять для создания прямого конфликта записи с записью. Продвижение: пусть одна транзакция "обновит" местоположение, предназначенное только для чтения (заменив значение тем же самым значением), чтобы создать прямой конфликт записи с записью (или используйте эквивалентное продвижение, например, Oracle's SELECT FOR UPDATE). В приведенном выше примере мы можем материализовать конфликт, добавив новую таблицу, которая сделает скрытое ограничение явным, сопоставляя каждого человека с его общим балансом. Фил начнет с общего баланса в 200 долларов, и каждая транзакция будет пытаться вычесть 200 долларов из этого баланса, создавая конфликт записи с записью, который не позволит обеим транзакциям успешно завершиться одновременно. Однако такой подход нарушает нормальную форму. В качестве альтернативы, мы можем "преобразовать" одно из чтений транзакции в запись. Например, T2 может установить V1 = V1, создавая искусственный конфликт записи с записью с T1 и, опять же, предотвращая одновременное успешное завершение обеих транзакций. Такое решение не всегда возможно. В целом, поэтому, изоляция снимков перекладывает часть задачи поддержания нетривиальных ограничений на пользователя, который может не осознавать потенциальные подводные камни или возможные решения. Преимущество такого подхода – повышенная производительность.

Терминология

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

История

Изоляция моментальных снимков возникла в результате работы над базами данных с многоверсионным управлением параллельным доступом, где одновременно поддерживаются несколько версий базы данных, чтобы читатели могли выполнять операции, не конфликтуя с писателями. Такая система позволяет естественно определить и реализовать данный уровень изоляции. К сожалению, стандарт ANSI SQL 92 был разработан с учетом баз данных, основанных на блокировках, и поэтому является довольно нечетким применительно к системам MVCC. Беренсон и др. в 1995 году опубликовали работу, в которой отмечалось, что эта реализация сериализуемости хорошо подходит для баз данных с многоверсионным управлением параллельным доступом и была внедрена в PostgreSQL 9.1, где она известна как Serializable Snapshot Isolation (SSI). При последовательном использовании это устраняет необходимость в вышеупомянутых обходных решениях. Недостатком по сравнению с изоляцией моментальных снимков является увеличение числа прерванных транзакций. Её производительность может быть как выше, так и ниже, чем у изоляции моментальных снимков с вышеупомянутыми обходными решениями, в зависимости от рабочей нагрузки.