Объектные блокировки. Схемы блокировок в базах данных

Одновременный конкурентный доступ может вызывать разные отрицательные эффекты, например чтение несуществующих данных или потерю модифицированных данных. Рассмотрим следующий практический пример, иллюстрирующий один из этих отрицательных эффектов, называемый грязным чтением. Пользователь U1 из отдела кадров получает извещение, что сотрудник "Василий Фролов" поменял место жительства. Он вносит соответствующее изменение в базу данных для данного сотрудника, но при просмотре другой информации об этом сотруднике он понимает, что изменил адрес не того человека. (В компании работают два сотрудника по имени Василий Фролов.) К счастью, приложение позволяет отменить это изменение одним нажатием кнопки. Он нажимает эту кнопку, уверенный в том, что данные после отмены операции изменения адреса уже не содержат никакой ошибки.

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

Чтобы предотвратить подобные проблемы в модели пессимистического одновременного конкурентного доступа, которую мы кратко описали в предыдущей статье, каждая система управления базами данных должна обладать механизмом для управления одновременным доступом к данным всеми пользователями. Для обеспечения согласованности данных в случае одновременного обращения к данным несколькими пользователями компонент Database Engine, подобно всем СУБД, применяет блокировки. Каждая прикладная программа блокирует требуемые ей данные, что гарантирует, что никакая другая программа не сможет модифицировать эти данные. Когда другая прикладная программа пытается получить доступ к заблокированным данным для их модификации, то система или завершает эту попытку ошибкой, или заставляет программу ожидать снятия блокировки.

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

Режимы блокировки и уровень гранулярности блокировки рассматриваются в следующих двух разделах. Последующее обсуждение относится к модели пессимистического одновременного конкурентного доступа. Модель оптимистического одновременного конкурентного доступа основана на управлении версиями строк и рассматривается в последующих статьях.

Режимы блокировки

Режимы блокировки определяют разные типы блокировок. Выбор определенного режима блокировки зависит от типа ресурса, который требуется заблокировать. Для блокировок ресурсов уровня строки и страницы применяются следующие три типа блокировок:

Разделяемая блокировка (shared lock)

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

Монопольная блокировка (exclusive lock)

Резервирует страницу или строку для монопольного использования одной транзакции. Блокировка этого типа применяется инструкциями DML (INSERT, UPDATE и DELETE), которые модифицируют ресурс. Монопольную блокировку нельзя установить, если на ресурс уже установлена разделяемая или монопольная блокировка другим процессом, т.е. на ресурс может быть установлена только одна монопольная блокировка. На ресурс (страницу или строку) с установленной монопольной блокировкой нельзя установить никакую другую блокировку.

Блокировка обновления (update lock)

Может быть установлена на ресурс только при отсутствии на нем другой блокировки обновления или монопольной блокировки. С другой стороны, этот тип блокировки можно устанавливать на объекты с установленной разделяемой блокировкой. В таком случае блокировка обновления накладывает на объект другую разделяемую блокировку. Если транзакция, которая модифицирует объект, подтверждается, и у объекта нет никаких других блокировок, блокировка обновления преобразовывается в монопольную блокировку. У объекта может быть только одна блокировка обновления.

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

Возможность совмещения разных типов блокировок приводится в таблице ниже:

Эта таблица интерпретируется следующим образом: предположим транзакция T1 имеет блокировку, указанную в заголовке соответствующей строки таблицы, а транзакция T2 запрашивает блокировку, указанную в соответствующем заголовке столбца таблицы. Значение "Да" в ячейке на пресечении строки и столбца означает, что транзакция T2 может иметь запрашиваемый тип блокировки, а значение "Нет", что не может.

Компонент Database Engine также поддерживает и другие типы блокировок, такие как кратковременные блокировки (latch lock) и взаимоблокировки (spin lock).

На уровне таблицы существует пять разных типов блокировок:

    разделяемая (shared, S);

    монопольная (exclusive, X);

    разделяемая с намерением (intent shared, IS);

    монопольная с намерением (intent exclusive, IX);

    разделяемая с монопольным намерением (shared with intent exclusive, SIX).

Разделяемые и монопольные типы блокировок для таблицы соответствуют одноименным блокировкам для строк и страниц. Обычно блокировка с намерением (intent lock) означает, что транзакция намеревается блокировать следующий нижележащий в иерархии объектов базы данных ресурс. Таким образом, блокировка с намерением помещается на уровне иерархии объектов, который выше того объекта, который этот процесс намеревается заблокировать. Это является действенным способом узнать, возможна ли подобная блокировка, а также устанавливается запрет другим процессам блокировать более высокий уровень, прежде чем процесс может установить требуемую ему блокировку.

Возможность совмещения разных типов блокировок на уровне таблиц базы данных приведена в таблице ниже. Эта таблица интерпретируется точно таким же образом, как и предыдущая таблица.

Возможность совмещения разных типов блокировок
S X IS SIX IX
S Да Нет Да Нет Нет
X Нет Нет Нет Нет Нет
IS Да Нет Да Да Да
SIX Нет Нет Да Нет Нет
IX Нет Нет Да Нет Да

Гранулярность блокировки

Гранулярность блокировки определяет, какой ресурс блокируется в одной попытке блокировки. Компонент Database Engine может блокировать следующие ресурсы: строки, страницы, индексный ключ или диапазон индексных ключей, таблицы, экстент, саму базу данных. Система выбирает требуемую гранулярность блокировки автоматически.

Строка является наименьшим ресурсом, который можно заблокировать. Блокировка уровня строки также включает как строки данных, так и элементы индексов. Блокировка на уровне строки означает, что блокируется только строка, к которой обращается приложение. Поэтому все другие строки данной таблицы остаются свободными и их могут использовать другие приложения. Компонент Database Engine также может заблокировать страницу, на которой находится подлежащая блокировке строка.

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

Блокироваться также могут единицы дискового пространства, которые называются экстентами и имеют размер 64 Кбайт. Экстенты блокируются автоматически, и когда растет таблица или индекс, то для них требуется выделять дополнительное дисковое пространство.

Гранулярность блокировки оказывает влияние на одновременный конкурентный доступ. В общем, чем выше уровень гранулярности, тем больше сокращается возможность совместного доступа к данным. Это означает, что блокировка уровня строк максимизирует одновременный конкурентный доступ, т.к. она блокирует всего лишь одну строку страницы, оставляя все другие строки доступными для других процессов. C другой стороны, низкий уровень блокировки увеличивает системные накладные расходы, поскольку для каждой отдельной строки требуется отдельная блокировка. Блокировка на уровне страниц и таблиц ограничивает уровень доступности данных, но также уменьшает системные накладные расходы.

Укрупнение блокировок

Если в процессе транзакции имеется большое количество блокировок одного уровня, то компонент Database Engine автоматически объединяет эти блокировки в одну уровня таблицы. Этот процесс преобразования большого числа блокировок уровня строки, страницы или индекса в одну блокировку уровня таблицы называется укрупнением блокировок (lock escalation) . Порогом укрупнения называется граница, на которой система баз данных применяет укрупнение блокировок. Пороги укрупнения устанавливаются динамически системой и не требуют настройки. (В настоящее время пороговым значением укрупнения блокировок является 5000 блокировок.)

Основной проблемой, касающейся укрупнения блокировок, является то обстоятельство, что решение, когда осуществлять укрупнение, принимает сервер баз данных, и это решение может не быть оптимальным для приложений, имеющих различные требования. Механизм укрупнения блокировок можно модифицировать с помощью инструкции ALTER TABLE. Эта инструкция поддерживает параметр TABLE и имеет следующий синтаксис:

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

В примере ниже показана отмена укрупнения блокировок для таблицы Employee:

USE SampleDb; ALTER TABLE Employee SET (LOCK_ESCALATION = DISABLE);

Настройка блокировок

Настройку блокировок можно осуществлять, используя подсказки блокировок (locking hints) или параметр LOCK_TIMEOUT инструкции SET. Эти возможности описываются в следующих разделах.

Подсказки блокировок (locking hints)

Подсказки блокировок задают тип блокировки, используемой компонентом Database Engine для блокировки табличных данных. Подсказки блокировки уровня таблиц применяются, когда требуется более точное управление типами блокировок, накладываемых на ресурс. (Подсказки блокировок перекрывают текущий уровень изоляции для сеанса.)

Все подсказки блокировок указываются в предложении FROM инструкции SELECT. Далее приводится список и краткое описание доступных подсказок блокировок:

UPDLOCK

Устанавливается блокировка обновления для каждой строки таблицы при операции чтения. Все блокировки обновления удерживаются до окончания транзакции.

TABLOCK

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

ROWLOCK

Существующая разделяемая блокировка таблицы заменяется разделяемой блокировкой строк для каждой отвечающей требованиям строки таблицы.

PAGLOCK

Разделяемая блокировка таблицы заменяется разделяемой блокировкой страницы для каждой страницы, содержащей указанные строки.

NOLOCK

Синоним для READUNCOMMITTED, который мы рассмотрим при обсуждении уровней изоляции.

HOLDLOCK

Синоним для REPEATABLEREAD.

XLOCK

Устанавливается монопольная блокировка, удерживаемая до завершения транзакции. Если подсказка xlock указывается с подсказкой rowlock, paglock или tablock, монопольные блокировки устанавливаются на соответствующем уровне гранулярности.

READPAST

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

Все эти параметры можно объединять вместе в любом имеющем смысл порядке. Например, комбинация подсказок TABLOCK с PAGLOCK не имеет смысла, поскольку каждая из них применяется для разных ресурсов.

Параметр LOCK_TIMEOUT

Чтобы процесс не ожидал освобождения блокируемого объекта до бесконечности, можно в инструкции SET использовать параметр LOCK_TIMEOUT. Этот параметр задает период в миллисекундах, в течение которого транзакция будет ожидать снятия блокировки с объекта. Например, если вы хотите чтобы период ожидания был равен восемь секунд, то это следует указать следующим образом:

SET LOCK_TIMEOUT 8000

Если данный ресурс не может быть предоставлен процессу в течение этого периода времени, инструкция завершается аварийно и выдается соответствующее сообщение об ошибке. Значение LOCK_TIMEOUT равное -1 (значение по умолчанию) указывает отсутствие периода ожидания, т.е. транзакция не будет ожидать освобождения ресурса совсем. (Подсказка блокировки READPAST предоставляет альтернативу параметру LOCK_TIMEOUT.)

Отображение информации о блокировках

Наиболее важным средством для отображения информации о блокировках является динамическое административное представление sys.dm_tran_locks . Это представление возвращает информацию о текущих активных ресурсах диспетчера блокировок. Каждая строка представления отображает активный в настоящий момент запрос на блокировку, которая была предоставлена или предоставление которой ожидается. Столбцы представления соответствуют двум группам: ресурсам и запросам. Группа ресурсов описывает ресурсы, на блокировку которых делается запрос, а группа запросов описывает запрос блокировки. Наиболее важными столбцами этого представления являются следующие:

    resource_type - указывает тип ресурса;

    resource_database_id - задает идентификатор базы данных, к которой принадлежит данный ресурс;

    request_mode - задает режим запроса;

    request_status - задает текущее состояние запроса.

В примере ниже показан запрос, использующий представление sys.dm_tran_locks для отображения блокировок в состоянии ожидания:

Взаимоблокировки

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

USE SampleDb; BEGIN TRANSACTION BEGIN TRANSACTION UPDATE Works_on UPDATE Employee SET Job = "Менеджер" SET DepartamentNumber = "d2" WHERE EmpId = 25348 WHERE Id = 28559 AND ProjectNumber = "p2" WAITFOR DELAY "00:00:10" WAITFOR DELAY "00:00:10" UPDATE Employee DELETE FROM Works_on SET LastName = "Фролова" WHERE EmpId = 25348 WHERE LastName = "Вершинина" AND ProjectNumber = "p2" COMMIT COMMIT

Если обе транзакции в примере выше будут выполняться в одно и то же время, то возникнет взаимоблокировка и система возвратит следующее сообщение об ошибке:

Сообщение 1205, уровень 13, состояние 45 Транзакция (процесс с идентификатором 56) находится во взаимной блокировке с другим процессом и выбрана в качестве потерпевшей взаимоблокировки. Повторите выполнение команды.)

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

Вы можете повлиять на то, какая транзакция будет выбрана системой в качестве "жертвы" взаимоблокировки, присвоив в инструкции SET параметру DEADLOCK_PRIORITY один из 21 (от -10 до 10) разных уровней приоритета взаимоблокировки. Константа LOW соответствует значению -5, NORMAL (значение по умолчанию) - значению 0, а константа HIGH - значению 5. Сеанс "жертва" выбирается в соответствии с приоритетом взаимоблокировки сеанса.

Блокирование и разблокирование

Если Вы предпочитаете использовать какие-то другие утилиты для создания резервных копий базы данных или просто делать обычную копию базы данных как резервную, то в игру вступает режим блокировки/разблокировки программы nbackup . «Блокировка » в данном случае означает, что основной файл базы данных временно замораживается, а не невозможность внесения изменений в базу данных. Как и в режиме резервирования, изменения фиксируются во временном файле дельты; при разблокировании файл дельты объединяется с основным файлом базы данных.

В качестве напоминания: nbackup.exe находится в подпапке bin папки, куда установлена СУБД Firebird. Типичными местонахождениями, например, являются C:\Program Files\Firebird\Firebird_2_0\bin (Windows) или /opt/firebird/bin (Linux). У утилиты нет графического интерфейса; Вы запускаете ее из командной строки (или из пакетного файла, или из другого приложения).

Блокировка базы данных и самостоятельное резервное копирование

Типичным сценарием является следующий:

    Блокировать базу данных с помощью параметра -L (Lock):

    nbackup [-U <пользователь> -P <пароль> ] -L <база_данных>
  1. Теперь можно создать резервную копию, сжать файл базы данных (и много еще чего можно делать с содержимым файла базы данных), используя Ваши любимые программы. Простое копирование файла также допустимо.

    Разблокировать базу данных с помощью параметра -N (uNlock):

    nbackup [-U <пользователь> -P <пароль> ] -N <база_данных>

Последняя команда также приведет к объединению файла дельты, куда записывались все изменения (мета)данных за время блокировки, с основным файлом базы данных.

Созанная резервная копия будет содержать данные, которые являлись актуальными на момент начала блокировки; эти данные не зависят от длительности блокировки, а также от периода времени, прошедшего с начала блокировки до момента начала резервного копирования.

Внимание

Что применимо к резервированию/восстановлению, также применимо и к блокированию/разблокированию: не используйте блокирование/разблокирование на многофайловых базах данных. Пока ситуация не изменится, не подпускайте nbackup к многофайловым базам данных!

Восстановление из резервной копии, сделанной после выполнения "nbackup -L"

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

  1. Разархивируйте/скопируйте/восстановите файл базы данных с помощью используемых Вами утилит.

    Теперь разблокируйте базу данных, но не с параметром -N , а с параметром -F (Fixup):

    nbackup -F <база_данных>

Почему существует два параметра командной строки, -N и -F ?

  • При использовании параметра -N сначала определяется наличие любых изменений с момента блокирования базы данных (после использования параметра -L ) и производится объединение временного файла дельты и основного файла базы данных. После этого база данных переводится в нормальный режим чтения/записи, а временный файл удаляется.

    При использовании параметра -F только изменяется в «нормальное » значение флага состояния самостоятельно восстановленной базы данных.

Итак, Вы используете:

    параметр -N после создания резервной копии своими силами (для возвращения флага состояния после ранее выполненного блокирования файла с параметром -L );

    параметр -F после самостоятельного восстановления из такой резервной копии.

Замечание

Не очень хорошо получилось, что последний параметр -F назван по слову Fixup (поправить): его предназначение не исправлять что-либо, а только разблокировать базу данных. Параметр -N (uNlock, разблокировать), с другой стороны, не только разблокирует базу данных, но и вносит в нее некоторые правки (внедряет сделанные изменения в базу данных). Однако, нам придется работать с тем, что есть.

для него метод экземпляра destroy. Семантическое различие трудноуловимо, но становится понятным, когда задаются обратные вызовы before_destroy или возникают зависимые ассоциации, то есть дочерние объекты, которых нужно автоматически удалить вместе с родителем.

Блокировка базы данных

Термином блокировка обозначается техника, позволяющая предотвратить обновление одних и тех же записей несколькими одновременно работающими пользователями. При загрузке строк таблицы в модель ActiveRecord по умолчанию вообще не применяет блокировку. Если в некотором приложении Rails в любой момент времени обновлять данные может только один пользователь, то беспокоиться о блокировках не нужно.

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

конкурентностью . Спросите себя, какиеколлизии илигонки (race conditions) могут иметь место, если два пользователя попытаются обновить модель в один и тот же момент?

К учету конкурентности в приложениях, работающих с базой данных, есть несколько подходов. В ActiveRecord встроена поддержка двух из них: оптимистической ипессимистической блокировки. Есть и дру-

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

Оптимистическая блокировка

Оптимистическая стратегия блокировки заключается в обнаружении и разрешении конфликтов по мере их возникновения. Обычно ее рекомендуют применять в тех случаях, когда коллизии случаются нечасто. При оптимистической блокировке записи базы данных вообще не блокируются, так что название только сбивает с толку.

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

Если вы контролируете схему базы данных, то реализовать оптимистическую блокировку совсем просто. Достаточно добавить в таблицу целочисленную колонку с именем lock_version и значением по умолчанию 0:

Блокировка базы данных

AddLockVersionToTimesheets < ActiveRecord::Migration

add_column:timesheets, :lock_version, :integer, :default => 0

remove_column:timesheets, :lock_version end

Само наличие такой колонки изменяет поведение ActiveRecord. Если некоторая запись загружена в два экземпляра модели и сохранена с разными значениями атрибутов, то успешно завершится обновление первого экземпляра, а при обновлении второго будет возбуждено ис-

ключение ActiveRecord::StaleObjectError.

Для иллюстрации оптимистической блокировки напишем простой автономный тест:

class TimesheetTest < Test::Unit::TestCase

fixtures:timesheets, :users

def test_optimistic_locking_behavior first_instance = Timesheet.find(1) second_instance = Timesheet.find(1)

first_instance.approver = users(:approver) second_instance.approver = users(:approver2)

assert first_instance.save, "Успешно сохранен первый экземпляр"

assert_raises ActiveRecord::StaleObjectError do second_instance.save

Тест проходит, потому что при вызове save для второго экземпляра мы ожидаем исключения ActiveRecord::StaleObjectError. Отметим,

что метод save (без восклицательного знака) возвращает false и не возбуждает исключений, если сохранение не выполнено из-за ошибки контроля данных . Другие проблемы, например блокировка записи, могут приводить к исключению. Если вы хотите, чтобы колонка, содержащая номер версии, называлась неlock_version , а как-то иначе, измените эту настройку с помощью метода set_locking_column. Чтобы это изменение действовало глобально, добавьте в файлenvironment.rb такую строку:

ActiveRecord::Base.set_locking_column "alternate_lock_version"

Как и другие настройки ActiveRecord, эту можно задать на уровне модели, если включить в класс модели такое объявление:

class Timesheet < ActiveRecord::Base set_locking_column "alternate_lock_version"

Обработка исключения StaleObjectError

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

Если обновляемые данные очень важны, вы, возможно, захотите по­ тратить время на то, чтобы смастерить дружелюбную к пользователю систему, каким-то образом сохраняющую изменения, которые тот пытался внести. Если же данные легко ввести заново, то как минимум сообщите пользователю, что обновление не состоялось. Ниже приведен код контроллера, в котором реализован такой подход:

def update begin

@timesheet = Timesheet.find(params[:id]) @timesheet.update_attributes(params[:timesheet])

# куда-нибудь переадресовать rescue ActiveRecord::StaleObjectError

flash[:error] = "Табель был модифицирован, пока вы его редактировали." redirect_to:action => "edit", :id => @timesheet

У оптимистической блокировки есть ряд преимуществ. Для нее не нужны специальные механизмы СУБД, и реализуется она сравнительно просто. Как видно из примера, для обработки исключения Stale­ ObjectError потребовалось очень немного кода.

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

Пессимистическая блокировка

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

При работе с объектными данными (справочники, документы, планы счетов и т.д.) система «1С:Предприятие» обеспечивает два вида объектных блокировок: пессимистическую и оптимистическую. Они позволяют выполнять целостные изменения объектов при одновременной работе нескольких пользователей.

Объектная пессимистическая блокировка

Пессимистическая объектная блокировка предназначена для запрета изменений данных объекта, пока блокировка не будет снята. Система (с помощью соответствующих расширений формы объекта) автоматически устанавливает пессимистическую блокировку, в момент, когда пользователь пытается произвести изменение данных объекта. Если после этого другой пользователь, например, попытается выполнить редактирование того же объекта, ему будет выдано сообщение о том, что не удалось заблокировать объект. Когда форма будет закрыта пользователем, данная блокировка будет снята.

Рассмотрим пример.
Войдем в учебную информационную базу под пользователем «Васильев В.В.», откроем форму документа «Поступление товаров 00000000001 от 01.06.2016» и внесем изменения в поле комментарий (рис. 1.3).

Не сохраняя документ войдем в информационную базу под пользователем «Иванов И.И.», откроем тот же документ и попробуем внести изменения в любом реквизите документа. Система не даст нам внести изменения и выдаст сообщение об ошибке (рис. 1.4).

Отсюда следует, что пессимистическая блокировка гарантирует, что пользователь, начав изменять данные объекта, сможет записать эти изменения в информационную базу.

Разработчик используя средства встроенного языка может задействовать пессимистическую блокировку. Используя метод «Заблокировать()» происходит установка пессимистической объектной блокировки, а для снятия используется метод «Разблокировать()».


Давайте рассмотрим другой пример. Под пользователем «Васильев В.В.» в разделе «Нормативно-справочная информация» откроем элемент справочника «Склады» с наименованием «Склад №1» и внесем изменения в наименование (рис. 1.5).


Не сохраняя переключимся в окно информационной базы, который был запущен под пользователем «Иванов В.В.», в разделе «Нормативно-справочная информация» откроем обработку «Удаление объекта». Выберем в качестве удаляемого объекта выберем элемент справочника «Склады» с наименованием «Склад №1» и нажмем «Удалить объект» (рис. 1.6).

В результате система позволит удалить данный элемент справочника и система не выдаст сообщение об ошибке. Дело в том, что операция блокирования не препятствует операции изменения или удаления объекта в базе данных.

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

Есть два способа проверки:

  1. Метод «Заблокирован()» используется для проверки блокировки объекта базы данных текущим сеансом. Данный метод не предоставляет возможность проверки заблокирован ли объект вообще.
  2. Для проверки заблокирован объект базы данных вообще используется метод «Заблокировать()». Попытка блокировки заблокированного объекта вызывает исключение, которое может быть обработано конструкцией «Попытка…..Исключение…..КонецПопытки».

Пессимистическая блокировка в управляемых формах

При работе с управляемыми формами методы «Заблокировать()», «Разблокировать()» и «Заблокирован()» могут не подойти из-за специфики работы управляемого приложения.

Дело в том, что данные методы используются для объектов базы данных. Объект базы данных существует только на сервере. Получается разработчику придется выполнить серверный вызов, получить объект базы данных путем преобразования основного реквизита формы с помощью метода формы «РеквизитФормыВЗначение». Далее вызывается один из методов объекта «Заблокировать()», «Разблокировать()» или «Заблокирован()». Но данный способ блокировки будет бесполезен, если задача стоит, чтобы объект был заблокирован пока открыта форма, так как полученный объект будет жить до конца серверного вызова.

Для работы с блокировками из управляемой формы необходимо использовать методы: «ЗаблокироватьДанныеФормыДляРедактирования()» и «РазблокироватьДанныеФормыДляРедактирования()». Данные методы используются для блокировки или разблокировки данных основного реквизита формы.

Рассмотрим пример. В разделе «Нормативно-справочная информация» откроем любой элемент справочника «Номенклатура» под пользователем «Васильев В.В.», внесем изменения в наименование и не сохраняя под пользователем «Иванов И.И.» отроем тот же элемент справочника. При попытки внесения изменений система выдаст сообщение об ошибке.


Далее в форме элемента справочника под пользователем «Васильев В.В.» нажмем на кнопку «Разблокировать» (рис. 1.7) и попробуем снова внести изменения в данный элемент справочника под пользователем «Иванов И.И.». В данном случае система даст внести изменения и записать элемент справочника.


Для отключения пессимистической блокировки в управляемых формах в свойстве основного реквизита надо снять флаг «Сохраняемые данные». Данный флаг определяет будет ли при интерактивном редактировании блокироваться данные основного реквизита, или нет рис(1.8).

Объектная оптимистическая блокировка

Оптимистическая блокировка представляет собой проверку, которая выполняется перед записью объекта в базу данных. У объекта есть свойство «ВерсияДанных», которая вместе с объектом считывается из базы данных. Оптимистическая блокировка производит перед записью производит сравнение значения свойства «ВерсияДанных» объекта, который находится в оперативной памяти с значением свойства «ВерсияДанных» объекта находящийся в базе данных. Если значения свойства «ВерсияДанных» у объектов отличается, то оптимистическая блокировка запрещает запись объекта в базу данных и выдает сообщение об ошибке.

Рассмотрим пример.

В разделе «Нормативно-справочная информация» откроем любой элемент справочника «Номенклатура» под пользователем «Васильев В.В.», далее не закрывая форму элемента под пользователем «Иванов И.И.» в разделе «Нормативно-справочная информация» откроем обработку «Изменить объект».

В обработке выберем ту же номенклатуру и нажмем кнопку «Изменить объект». Данная команда добавит в конце наименования «!!!» (рис. 1.9).

После изменения попробуем записать открытый элемент справочника номенклатуры под пользователем «Васильев В.В.». Система выдаст предупреждение о том, что данные объекты были изменены или удалены и не даст записать данный объект (рис. 1.10).

Для отключения оптимистической блокировки необходимо перед записью объекта находящийся в оперативной памяти сравнить версию с версией объекта базы данных. Если версии данных отличаются, то получаем объект из базы данных и переносим изменения в него, далее записываем.