Почему при повторном чтении может происходить перекос записи?

Wiki говорит: ;

Повторяющееся чтение:
На этом уровне изоляции реализация СУБД с контролем параллелизма на основе блокировок сохраняет блокировки чтения и записи (полученные для выбранных данных) до конца транзакции. Однако блокировки диапазона не управляются, поэтому могут происходить фантомные чтения.

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

Мне любопытно, почему write skew может случиться в Repeatable reads? В нем говорится, что он будет сохранять блокировки чтения и записи до конца транзакции, а write skew происходит, когда previously read the columns they are updating, так как же заблокировать блокировку записи, когда блокировка чтения заблокирована?


person handora    schedule 24.01.2018    source источник


Ответы (1)


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

Многие базы данных, такие как Postgres, SQLServer с уровнем изоляции повторяемого чтения, могут обнаруживать потерянное обновление ( особый случай перекоса записи), но другие этого не делают. (т.е. движок InnoDB в MySQL)

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

Я беру пример из книги Разработка приложений с интенсивным использованием данных. Вот сценарий:

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

Следующий интересный вопрос - как это реализовать в базах данных. Вот псевдокод SQL-кода:

BEGIN TRANSACTION;
    SELECT * FROM doctors
        WHERE on_call = true
        AND shift_id = 1234;
    if (current_on_call >= 2) {
        UPDATE doctors
        SET on_call = false WHERE name = 'Alice' AND shift_id = 1234;
    }
COMMIT;  

Вот иллюстрация:  Данные потока

Как показано на приведенной выше иллюстрации, Боб и Алиса одновременно работают над кодом SQL. Однако Боб и Алиса изменяли разные данные, Боб изменил запись Боба, а Алиса изменила запись Алисы. Базы данных на уровне изоляции повторяемого чтения никоим образом не могут узнать и проверить, что условие (общее число врачей> = 2) было нарушено. Произошло явление перекоса записи.

Для решения этой проблемы предлагается 2 метода:

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

Вот псевдокод с использованием SELECT .. FOR UPDATE запроса.

BEGIN TRANSACTION;
    SELECT * FROM doctors
        WHERE on_call = true
        AND shift_id = 1234 FOR UPDATE; // important here: locks all records that satisfied requirements.

    if (current_on_call >= 2) {
        UPDATE doctors
        SET on_call = false WHERE name = 'Alice' AND shift_id = 1234;
    }
  COMMIT;  
  1. Использование более строгого уровня изоляции. Оба MySQL, Postgres T-SQL обеспечивает уровень изоляции сериализации .
person hqt    schedule 09.09.2018
comment
Спасибо за очень четкое объяснение. У меня есть одно замечание. Вы сказали, что MySQL Serializeable IL может предотвращать явление перекоса записи по умолчанию, верно? MySQL manual утверждает, что в Serializeable IL InnoDB implicitly converts all plain SELECT statements to SELECT ... FOR SHARE. Я думаю, это означает, что даже с Serializeable IL в MySQL чистая разделяемая блокировка 2PL не может предотвратить перекос записи в описанном сценарии, поскольку обе транзакции все еще могут читать строки и оценивать оператор if параллельно. - person Suren Aznauryan; 27.11.2018
comment
Продолжая свой предыдущий комментарий: на самом деле вы все правильно написали. В случае уровня изоляции Serializable в MySQL, предполагая такое же чередование операторов транзакций, как в вашем примере, обе транзакции будут блокировать S для каждой записи врача, тогда обе будут оценивать оператор current_on_call >= 2, что приводит к true, тогда обе транзакции будут заблокированы, как для при выполнении Update оператора им нужна X блокировка, но они не могут получить ее, поскольку другая транзакция удерживает S блокировок для всех записей о врачах. Результат - тупик. Будет выполнен откат одной транзакции - исключен перекос записи - person Suren Aznauryan; 27.11.2018
comment
Я написал блог, чтобы проверить уровень изоляции в MariaDB / MySQL. medium.com/@huynhquangthao/ Важная часть этого теста в тесте на Repeatable Read и Serializable. После тестирования с MySQL я вижу, что: - Изоляция повторяющегося чтения может обнаруживать фантомное чтение. Но вы все равно можете обновить запись, созданную другой транзакцией. - Сериализуемая изоляция может предотвратить такое поведение: другая транзакция не может изменять данные, которые могут изменить результат другой транзакции. - person hqt; 02.05.2019
comment
это объяснение кажется странным - разве изоляция повторяемого чтения не должна давать тот же результат для выбора, который был выполнен в транзакции? Значит, значение current_on_call не должно измениться? Мое базовое тестирование с SQL Server показывает, что обновление заблокировано для повторяемого чтения. - person Alexander Shelemin; 03.02.2021
comment
Думаю, я понял это - это зависит от деталей реализации изоляции повторяемого чтения. Postgres, например, основан на MVCC, поэтому он действительно не может предотвратить описанный перекос при записи, но SQL Server основан на блокировке (2PL), поэтому, насколько я могу судить, он предотвращает его. - person Alexander Shelemin; 04.02.2021