Чтобы “пощупать”, как ведёт себя система с разными настройками, я сделал элементарный пример с одной единственной таблицей – результаты экспериментов описаны ниже. Дополнительно выяснилось, что система ведёт себя по-разному не только с разными настройками, но и с одинаковыми настройками под разными СУБД (Postgre и MS SQL).
Итак, для тестирования мы создадим пустую конфигурацию 1С на платформе 8.3 (я использовал 8.3.8.1747), в режиме толстого клиента, с одним единственным справочником ДолжностиОрганизаций. В этот справочник добавим единственный элемент с кодом “ТестЭл” и наименованием “!Тестовый элемент”.
Для тестирования мы будем из двух параллельных сеансов читать, а затем попытаемся записать эту единственную запись, и будем наблюдать блокировки, возникающие при конкурентном обращении к одному и тому же ресурсу.
Делать это будем с помощь внешней обработки с простым кодом:
НачатьТранзакцию(); ТестЭлСсылка = Справочники.ДолжностиОрганизаций.НайтиПоКоду("ТестЭл"); Предупреждение("Прочитан: "+ТестЭлСсылка.Наименование); ТестЭлОбъект = ТестЭлСсылка.ПолучитьОбъект(); ТестЭлОбъект.Наименование = "!Тестовый элемент "+ТекущаяДата(); ТестЭлОбъект.Записать(); Если Вопрос("Записать? "+ТестЭлОбъект.Наименование,РежимДиалогаВопрос.ДаНет) = КодВозвратаДиалога.Да Тогда ЗафиксироватьТранзакцию(); Иначе ОтменитьТранзакцию(); КонецЕсли;
Для начала конфигурация под MS SQL у нас находится в режиме автоматических блокировок, что соответствует уровню изоляции repeatable read (повторяемое чтение) для Справочников:

Запускаем параллельно два сеанса, в обоих сеансах запускаем код обработки и проходим этап чтения справочника:

При нажатии на ОК в любом из окон далее происходит попытка записать этот элемент внутри транзакции. Поскольку каждым из сеансов на этот элемент установлена блокировка на чтение (котороя сохраняется до конца транзакции) – при попытке записи этого элемента под MS SQL получаем ошибку таймаута:

После ошибки таймаута транзакция во 2 сеансе откатывается, в 1 сеансе мы можем выполнить запись. На скриншоте запись элемента справочника уже выполнена, но сама транзакция ещё не завершилась:

При этом во 2 сеансе мы можем открыть форму списка справочника и увидеть там изменённое наименование элемента справочника, которое изменено не завершённой ещё транзакцией.

Это и есть иллюстрация “грязного чтения” (dirty read). Отображение формы списка справочника в данном случае осуществляется без учёта завершённости транзакций. При этом если в 1 сеансе мы нажмём НЕТ (отменим транзакцию) – только после обновления списка во 2 сеансе Наименование нашего тестового элемента вернётся в нетронутое состояние:

Интересно отметить, что с указанными настройками 1С база под PostgreSQL ведёт себя совершенно по-другому. После нажатия ОК после чтения PostgreSQL никакой таймаут не возвращает, а начинает спокойно ждать, когда освободится элемент от блокировки на чтение, чтоб его записать. Первый раз когда я с этим столкнулся – подождал минут 15 и хотел уже снимать повисший сеанс. Но после того как мы нажмём ОК во втором сеансе – возникает ошибка на взаимоблокировке (deadlock):

Это происходит потому, что наши 1 и 2 сеанс начинают ждать друг друга – каждый на попытке записи ждёт другой сеанс, пока тот снимет блокировку на чтение. После того как второй сеанс был выбран жертвой взаимоблокировки – его транзакция отменяется, и в первом сеансе выполняется код записи элемента внутри транзакции, о чём нам свидетельствует следующее диалоговое окно:

“Грязного чтения” при отображении списка Справочника в случае с PostgreSQL не происходит. Пока транзакция не завершилась – в другом сеансе мы видим в списке не изменённое название элемента справочника:

На этом рассмотрение режима repeatble read (повторяемое чтение, “автоматические блокировки” в 1С) мы закончим. Мораль repeatble read – если мы что-то прочитали в транзакции – никто из других транзакций не сможет изменить эти записи, пока наша транзакция не завершится (даже если мы ничего в ней записывать и не будем).
Теперь перейдём к рассмотрению режима read commited в MS SQL.
Для этого переведём конфигурацию в режим управляемых блокировок. Для этого переведём режим управления блокировками в состояние Управляемый и останемся в режиме совместимости с 8.2:

После применения изменений запустим в двух сеансах ту же обработку. Теперь при чтении элемента блокировка снимается после прочтения – и поэтому в одном из сеансов внутри транзакции мы можем осуществить запись элемента:

При попытке записать тот же элемент во втором сеансе получим ошибку-таймаут:

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

Это происходит потому что при выводе формы справочника данные читаются 1Сом вне транзакции, без учёта установленных блокировок. Если же мы попытаемся во 2 сеансе снова запустить обработку с начала – на этот раз получим таймаут уже на операции чтения:

Поскольку при записи элемента на него была установлена эксклюзивная блокировка, а транзакция в 1 сеансе ещё не завершилась.
Собственно, мораль read commited, в нашем случае: чтение данных в транзакции НЕ блокирует их автоматически от записи. Для принудительной блокировки нужно использовать объект БлокировкаДанных.
Ну и теперь переведём базу SQL в режим read commited snaphot, и попробуем понять, есть ли отличия работы в этом режиме? Режим совместимости базы установим в “Не использовать”:

При попытке параллельной записи элемента справочника во 2 сеансе мы также получаем таймаут, только его возвращает теперь не СУБД MS SQL, а сама платформа 1С:

Но теперь у нас исчезла проблема “грязного чтения” формы списка справочника:

И также мы можем повторно читать Наименование справочника во 2 сеансе в транзакции – читается Наименование, которое было до изменения, транзакция которая пишет это Наименование в 1 сеансе нам не мешает его читать:

Остаётся проверить: а как read commited работает на PostgreSQL?
При проверке оказывается, что под Postgre SQL база и в режиме совместимости с 8.2.13, и с отключенным режимом совместимости работает так, как MS SQL в режиме read commited snapshot, а именно:
Грязного чтения в форме списка справочника элемента, изменённого не завершённой транзакцией, не возникает.
Повторное чтение изменяемого внутри другой транзакции элемента – возможно.
Блокировка элемента, изменяемого внутри транзакции и выдача ошибки таймаута осуществляется на уровне платформы 1С:Предприятия, а не СУБД.
И это не удивительно, судя по документации PostgreSQL – то, что в MS SQL называется read commited snapshot – в PostgreSQL называется просто read commited, и данные читаются из snapshot базы без учёта незавершённых транзакций всегда. В PostgreSQL просто нет “read commited БЕЗ snapshot”.
Автор статьи – Илья Евгеньевич Петров
г. Самара, 2016 г.

Что-то странное, если мы не завершили транзакцию, то по идее не и не получим изменения. Объясните как после записи и незаконченной транзакции в одном сеансе, мы получили измененные данные в другом сеансе? Т.е. после записи транзакция не завершена и блокировка висит, при записи в другой транзакции мы получим таймаут на блокировке, но при повторном чтении, мы не получим данные измененные в первой транзакции, это невозможно. Любой уровень изоляции, кроме uncommitted read, защищает от грязного чтения. Включение режима RCSI позволяет получить согласованную версию данных до начала транзакции, если транзакция не закончилась. Т.е. например мы записываем набор записей в одной транзакции, а затем читаем тот же набор записей в другой транзакции, при включенном RCSI тайм-аута не будет, а при выключенном будет тайм-аут, так как в первой транзакции накладывается блокировка исключительная, а во второй разделяемая и нет версионирования.
На какой момент фиксируется “снимок” snapshot-а? Например начали транзакцию 1, выполняем какие то действия, но данные еще не читали и не записывали. В это время транзакция 2 изменила наши данные и завершилась. Транзакция 1 читает данные. Какие это будут данные? Те которые были до начала транзакции 1 или те которые получились в результате работы транзакции 2?
Добрый день! Первый и второй скриншоты с настройками совместимости неправильные.
На первом должно быть:
– Режим управления блокировкой данных – Автоматический;
– Режим совместимости – 8.2.13.
На втором должно быть:
– Режим управления блокировкой данных – Управляемый;
– Режим совместимости – 8.2.13.
На третьем все правильно:
– Режим управления блокировкой данных – Управляемый;
– Режим совместимости – Не использовать.
За статью спасибо!
Доброго дня, Александр!
Спасибо, статью исправили.
Спасибо, очень познавательный материал!
Одно замечания, второй скрин с настройками совместимости явно не верный (он такой-же как и третий), я так понимаю, там должно быть “Управляемый” и “Версия 8.2.13”.
Александр, спасибо!
Скриншот исправили.
Добрый день! Каким образом можно гарантированно избежать грязного чтения при формировании, например, отчета в 1С без использования read commited snaphot на SQL-сервере? Получается, что читать данные запросом, чтобы они всегда были “чистыми”, можно только в рамках транзакции, даже если в рамках этой транзакции мы сами ничего в базу не записываем?
Доброго дня, Мария!
Никак. Если транзакицю не начинать – будет грязное чтение (например, в консоли запросов). А если начинать транзакцию – будет таймаут, пока чужие блокировки не снимутся.