Subject: зачем нужно NO RECORD_VERSION |
From: "Vadim Guchenko" <s0lver@kraslan.ru> |
Date: Mon, 28 Jun 2004 20:54:10 +0800 |
Hello, All! Я проектировал базу данных таким образом, чтобы она не выдавала ошибок блокировки вообще, а в случае возникновения конфликтов ждала, пока блокирующая транзакция завершится, и продолжала свою работу (разумеется о настоящем deadlock здесь речь не идет, когда две транзакции взаимно блокируются, ожидая окончания друг друга, т.к. в нормально спроектированной базе такого быть не должно).
Для этих целей все транзакции должны стартовать с параметром WAIT и быть максимально быстрыми, чтобы надолго не блокировать работу других конкурентных транзакций. Очевидно, что уровень изоляции SNAPSHOT для транзакций, изменяющих данные, в данном случае не подойдет.
Решено было остановится на READ COMMITTED RECORD_VERSION. Прочитав много разных статей и документацию я был убежден, что такая транзакция, запущенная с параметром WAIT, в случае попадания на конфликт при изменении данных будет ждать окончания блокирующей транзакции, и независимо от того, чем та закончится - COMMIT или ROLLBACK, продолжит свою работу по изменению этих данных. Все оказалось не так.
Транзакция READ COMMITED RECORD_VERSION WAIT действительно может изменять данные, которые были изменены другой транзакцией, стартовавшей позже, и подтверждены по COMMIT, но только в том случае, если подтверждение было раньше, чем первая транзакция приступит к изменению этих данных и попадет на конфликт и последующее ожидание. Если случится конфликт, то в случае если блокирующая транзакция откатится, ожидающая транзакция сможет изменить данные и продолжит свою работу, однако если блокирующая транзакция подтвердится, то ожидающая транзакция вместо того, чтобы увидеть подтвержденные данные и изменить их снова, выйдет по ошибке deadlock, что является странным для READ COMMITTED транзакции. В этом случае ее поведение ничем не отличается от транзакции SNAPSHOT и практическое применение параметра WAIT кажется сомнительным.
Пример:
Две транзакции имеют параметры READ WRITE READ COMMITTED RECORD_VERSION WAIT.
Поскольку при обновлении записей всегда происходит их предварительное чтение (это видно в анализе производительности IB Expert'а), то объяснить такое поведение можно следующим (это предположение): транзакция 1 прочитала строку, которую нужно изменить, но при попытке непосредственного изменения натолкнулась на конфликт и стала ждать. А после завершения транзакции 2 оказалось, что версия записи в базе, которую нужно изменить, отличается от той, что была считана, и соответственно происходит выход по ошибке.
После этого разумным было проверить как поведут себя эти же транзакции с уровнем узоляции READ COMMITTED NO RECORD_VERSION. Оказалось, что описанная выше ситуация в этом случае обрабатывается правильно.
Пример:
Две транзакции имеют параметры READ WRITE READ COMMITTED NO RECORD_VERSION WAIT.
Однако сразу видно узкое место: если на одновременное изменение одних и тех же данных претендуют например три транзакции, то одна из транзакций успевает первой начать изменения, а две другие дойдут до чтения записи перед изменением и зависнут в ожидании окончания первой транзакции. Когда первая транзакция подтвердит изменения, ожидающие транзакции смогут одновременно начать чтение записи, которую они хотят изменить (это разрешено уровнем изоляции), и тут же записывать изменения в базу. Однако опять таки успеет начать запись только одна транзакция, а вторая попадет в ситуацию, описанную в случае с RECORD_VERSION, т.е. она будет ждать окончания изменяющей транзакции, но уже не перед чтением данных, а перед их записью, и в случае подтверждения изменений блокирующей транзакцией оставшаяся транзакция должна выдать ошибку.
Пример:
Три транзакции имеют параметры READ WRITE READ COMMITTED NO RECORD_VERSION WAIT.
Однако на практике все проходит без ошибок и все 3 изменения происходят последовательно одно за другим. Это говорит о том, что либо операция update выполняет чтение и запись измененных данных атомарно, либо подобные конфликты в случае с NO RECORD_VERSION WAIT были специально предусмотрены.
В любом случае у NO RECORD_VERSION есть еще одно полезное применение в изменяющих транзакциях, помимо ее бесполезного применения в читающих транзакциях.
With best regards, Vadim Guchenko. E-mail: s0lver@kraslan.ru