"На пальцах" - чем отличается "repeatable read" от "read commited" и "read commited snapshot"?

09.11.16

Разработка - Механизмы платформы 1С

В сети и в книгах довольно много информации с описанием уровней изоляции транзакций, их особенностей и отличий. Когда читаешь - всё вроде понятно, но при столкновении с практическими задачами возникают трудности. Чтобы "пощупать", как ведёт себя система с разными настройками, я сделал элементарный пример с одной единственной таблицей - результаты экспериментов описаны ниже. Дополнительно выяснилось, что система ведёт себя по-разному не только с разными настройками, но и с одинаковыми настройками под разными СУБД (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 сеансе нам не мешает его читать:

В этом и есть мораль snapshot-a: MS SQL работает в read commited snapshot в режиме версионирования записей - за пределами нашей изменяющей транзакции данные читаются из snapshot-а базы, и наши изменения данных внутри транзакции не вызывают блокировку на чтение этих данных вне этой транзакции.

Остаётся проверить: а как 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".

Таким образом, хотя таблица 3.6.2 на стр.41 книги Е.В.Филиппова "Настольная книга 1С:Эксперта по технологическим вопросам" и сообщает нам, что PostgreSQL в режиме управляемых блокировок использует уровень изоляции read commited - этот уровень изоляции в PostgreSQL соответсвует по поведению уровню read commited snapshot в MS SQL.

"repeateble read" "read commited" "read commited snapshot" "уровень изоляции транзакций"

См. также

Поинтегрируем: сервисы интеграции – новый стандарт или просто коннектор?

Обмен между базами 1C Администрирование СУБД Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

В платформе 8.3.17 появился замечательный механизм «Сервисы интеграции». Многие считают, что это просто коннектор 1С:Шины. Так ли это?

11.03.2024    4529    dsdred    53    

72

Как готовить и есть массивы

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Все мы используем массивы в своем коде. Это один из первых объектов, который дают ученикам при прохождении обучения программированию. Но умеем ли мы ими пользоваться? В этой статье я хочу показать все методы массива, а также некоторые фишки в работе с массивами.

24.01.2024    5294    YA_418728146    25    

63

Планы обмена VS История данных

Обмен между базами 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    6410    dsdred    36    

112

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    18473    SeiOkami    46    

118

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    12088    human_new    27    

74

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

28.08.2023    8822    YA_418728146    6    

141

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Внешние компоненты для 1С можно разработывать очень просто, пользуясь всеми преимуществами языка Rust - от безопасности и кроссплатформенности до удобного менеджера библиотек.

20.08.2023    6279    sebekerga    54    

94

Все скопируем и вставим! (Буфер обмена в 1С 8.3.24)

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Рассмотрим новую возможность 8.3.24 и как её можно эффективно использовать

27.06.2023    15986    SeiOkami    31    

103
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. Сурикат 393 10.11.16 14:11 Сейчас в теме
А кто-нибудь исследовал чем отличается инструкция (взятая с сайта Гилева):
USE [master]
GO
ALTER DATABASE [Моя База] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
ALTER DATABASE [Моя База]
SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE [Моя База]
SET READ_COMMITTED_SNAPSHOT ON
ALTER DATABASE [Моя База] SET MULTI_USER WITH ROLLBACK IMMEDIATE
GO

От того, что есть по умолчанию в 1С в режиме совмести 8.3.6 и выше?
9. Sergey.Noskov 1376 11.11.16 14:34 Сейчас в теме
(1) Сурикат, ничем не отличается, просто 8.3 это делает сама
16. Сурикат 393 11.11.16 23:10 Сейчас в теме
(9) Sergey.Noskov,
1С ставит только SET READ_COMMITTED_SNAPSHOT ON
Хотя на MSDN написано (https://msdn.microsoft.com/ru-ru/library/tcbchxcb(v=vs.110).aspx):
Перед использованием в транзакциях изоляция моментального снимка должна быть включена путем установки параметра базы данных ALLOW_SNAPSHOT_ISOLATION в значение ON. Это приводит к активизации механизма сохранения версий строк во временной базе данных (tempdb).

Собственно вопрос был:
Как работает READ_COMMITTED_SNAPSHOT без ALLOW_SNAPSHOT_ISOLATION?
17. headMade 144 12.11.16 10:04 Сейчас в теме
(16) Сурикат,
ссылка нерабочая
18. TMV 14 12.11.16 10:37 Сейчас в теме
(17) headMade, там последние 2 символа зацепило. Вот верная
2. max_st 292 10.11.16 15:44 Сейчас в теме
Пробовал, когда нужно было принудительно включить RCSI, но конфигурация была совместима только с 8.2. Работает.
3. CSiER 35 11.11.16 04:56 Сейчас в теме
Несколько вопросов по статье:
Собственно, мораль read commited, в нашем случае: чтение данных в транзакции НЕ блокирует их автоматически от записи. Для принудительной блокировки нужно использовать объект БлокировкаДанных.
- разве без БлокировкаДанных это будет не READ UNCOMMITTED?
Для начала конфигурация под MS SQL у нас находится в режиме автоматических блокировок, что соответствует уровню изоляции repeatable read
и ниже
Это и есть иллюстрация "грязного чтения" (dirty read)
- то есть при repeatable read получили dirty read?
• Не увидел ссылку на обработку.
Таким образом, хотя таблица 3.6.2 на стр.41 книги Е.В.Филиппова "Настольная книга 1С:Эксперта по технологическим вопросам" и сообщает нам, что PostgreSQL в режиме управляемых блокировок использует уровень изоляции read commited - этот уровень изоляции в PostgreSQL соответсвует по поведению уровню read commited snapshot в MS SQL.
- книга хорошая, написано верно, ведь Postgre чистый версионник, а ms sql - блокировочник.
Silenser; +1 Ответить
4. Makushimo 160 11.11.16 06:06 Сейчас в теме
(3) CSiER,
Поддержу вопрос коллеги.
Разве грязное чтение не исчезает на уровне изоляции READ COMMITED и выше?
6. headMade 144 11.11.16 12:39 Сейчас в теме
(3) CSiER,
Надо разделять менеджер блокировок MSQL и менеджер блокировок 1С.
Когда у вас включен автоматический режим блокировок, то работает только менеджер блокировок MSQL, при этом используется уровень изоляции транзакций repeatable read и serializable.
Причем это не сам MSQL решает какой уровень изоляции использовать при получении данных, а это сервер 1С при передаче запроса на выполнение MSQL "говорит" какой уровень изоляции для какой таблицы необходимо установить. Т.е. тут за блокировку полностью отвечает MSQL.

Когда вы включаете управляемый режим блокировок, то начинает работать менеджер блокировок 1С. И соответственно сервер 1С решает что для MSQL будет достаточным уровень изоляции транзакций read commited т.к. за блокировку данных будет отвечать сам сервер 1С. В этом случае MSQL фактически ничего не блокирует.
А "БлокировкаДанных " - это фактически команда серверу 1С, что необходимо наложить блокировку на определенные данные.
Т.е. не взирая на то используем мы или нет "БлокировкаДанных ", на уровне MSQL всегда будет использоваться read commited.
8. starik-2005 3033 11.11.16 14:17 Сейчас в теме
(6) headMade, а вот г-н Филиппов на курсах, помнится, говорит о том, что блокировки MS SQL все-таки устанавливает. В режиме 8.2 - реад комиттед, в режиме 8.3 - реад комиттед снапшот. В профайлере при этом нет инструкции WITH (NOLOCK), но даже наличие WITH (NOLOCK) не гарантирует их отсутствие.
10. herfis 498 11.11.16 15:03 Сейчас в теме
(8) Вы путаете блокировки с уровнями изоляции транзакций. И блокировки у MSSQL бывают разные, есть много вспомогательных. С практической точки зрения важно то, что в режиме изоляции "реад комиттед снапшот" читающие транзакции не "держат" пишущие транзакции и наоборот. И при этом прочитанные данные всегда согласованы, как при repeatable read.
13. headMade 144 11.11.16 16:17 Сейчас в теме
(8) starik-2005,
помнится, говорит о том, что блокировки MS SQL все-таки устанавливает.

Конечно же блокировки всегда устанавливаются. Важное значение имеет с каким уровнем изоляции они устанавливаются и когда они снимаются.
В некоторых случаях блокировка держится до конца транзакции (при repeatable read), а в некоторых снимается сразу после выполнения запроса (read commited).

но даже наличие WITH (NOLOCK) не гарантирует их отсутствие.
- естественно можно еще рассматривать блокировки стабильности схемы или блокировки намерений, но они на параллельность работы пользователей в 1С никак не влияют.
14. starik-2005 3033 11.11.16 16:34 Сейчас в теме
(13) лично я вот конкретно на это отвечал:
Когда вы включаете управляемый режим блокировок, то начинает работать менеджер блокировок 1С. И соответственно сервер 1С решает что для MSQL будет достаточным уровень изоляции транзакций read commited т.к. за блокировку данных будет отвечать сам сервер 1С. В этом случае MSQL фактически ничего не блокирует.


А есть такая еще штука, как эскалация блокировок. Может внезапно оказаться, что блокируется вся таблица в SQL, при том 1С вроде как заблокировала только часть данных (до 100к).
15. herfis 498 11.11.16 17:28 Сейчас в теме
(14)
А есть такая еще штука, как эскалация блокировок. Может внезапно оказаться, что блокируется вся таблица в SQL, при том 1С вроде как заблокировала только часть данных (до 100к).

Ну, эти страшилки остались в прошлом, слава богу. Такое было не редкость в режиме автоматических блокировок, где на MSSQL при проведении использовался serializable - самый строгий режим изоляции, беспощадный по блокировкам. В postgresql вообще были табличные блокировки.
А сейчас на версионниках с управляемыми блокировками одинэсник в самом деле фактически самолично управляет блокировками. Я не представляю, что нужно сделать, чтобы спровоцировать экскалацию блокировок СУБД в этой ситуации. Перезаписывать значительную часть большой таблицы в одной транзакции, разве что. Так тогда так и так разница невелика. И то помешает эта эскалация только другим транзакциям на запись.
19. qwinter 671 12.11.16 23:36 Сейчас в теме
(15) herfis,
Ну, эти страшилки остались в прошлом, слава богу.

обмен 500 новых контрагентов с 10 дополнительными значениями. Сущий пустяк правда?)
22. herfis 498 15.11.16 10:16 Сейчас в теме
(19)
обмен 500 новых контрагентов с 10 дополнительными значениями. Сущий пустяк правда?)

Скорее белиберда какая-то.
23. ture 606 21.12.16 10:06 Сейчас в теме
(19)мильёнами грузим ежедневно. Так что ЭТО пустяк.
7. headMade 144 11.11.16 12:44 Сейчас в теме
(3) CSiER,
то есть при repeatable read получили dirty read?


В статье же написано про 2 сеанса.
В первом сеансе у нас устанавливается блокировка с уровнем изоляции repeatable read.
А вот ВО ВТОРОМ сеансе у нас будет видно dirty read.

Т.к. в приведенном примере стоит уровень совместимости с 8.2, то read commited snaphot не используется. Соответственно когда пользователь во втором сеансе открывает форму списка, то фактически сервер 1С передает запрос MSQL с хинтом "with no lock". Что фактически означает что будут прочитаны все данные в т.ч. данные не зафиксированных транзакций. Поэтому именно ВО ВТОРОМ сеансе будет наблюдаться грязное чтение, невзирая на то что в первом сеансе на этих записях стоит блокировка с уровнем узоляции repeatable read.
12. CSiER 35 11.11.16 16:08 Сейчас в теме
(7) headMade, спасибо за ответ, вроде все понятно, но, чувствую, только повторение шагов автора с профайлером окончательно расставят все точки на "и" в моей голове )
5. starik-2005 3033 11.11.16 12:38 Сейчас в теме
В принципе то же самое рассказывают на курсах подготовки к эксперту/профессионалу по тех.вопросам. Единственное, что после курсов вопросы из комментов не возникают. ))
11. herfis 498 11.11.16 15:31 Сейчас в теме
Ну и да - включение режима "реад комиттед снапшот" по сути меняет поведение MSSQL с "блокировочника" на "версионник". MSSQL начинает фигачить версии изменяемых строк в tempdb и при чтении брать данные подходящих версий оттуда, а не из родных таблиц.
В отличие от старых уровней изоляции - это механизм совершенно другого качества. Поэтому его еще надо явно активировать в свойствах конкретной БД.
20. Yashazz 4709 14.11.16 15:04 Сейчас в теме
А, ещё один пересказ общедоступных источников...
21. Makushimo 160 15.11.16 05:40 Сейчас в теме
(20) Yashazz,
Ну не вредничайте.
Статья полезная, а общедоступные источники еще нужно найти и вычитать там именно то что нужно.
Статья хорошая
Andreyyy; Aleskey_K; Capitullo; kuzyara; ilya_petrov; +5 Ответить
Оставьте свое сообщение