Как узнать кто держит таблицу транзакцией
Перейти к содержимому

Как узнать кто держит таблицу транзакцией

  • автор:

SQL-Ex blog

Управление параллельным выполнением транзакций с помощью блокировок в SQL Server

Добавил Sergey Moiseenko on Среда, 16 марта. 2022

В многопользовательской среде важно поддерживать конкурирующие транзакции. Связанные с этим блокировки представляют собой структуры в памяти размером 96 байт. Их роль заключается в поддержке целостности данных, согласованности, управлении параллелизмом для каждой транзакции. SQL Server следует требованиям ACID для каждой транзакции.

  • Atomicity (атомарность): это свойство гарантирует, что транзакция, включающая два и более процессов, выполняется полностью, или ни один из процессов не будет зафиксирован.
  • Consistency (согласованность): это дает гарантию состояния зафиксированной транзакции. Транзакция должна либо создавать новое состояние данных, либо возвращать предыдущее состояние.
  • Isolation (изоляция): это означает, что транзакции изолированы друг от друга. Если транзакция выполняется и еще не зафиксировала данные, она изолирована от других транзакций.
  • Durability (длительность): длительность гарантирует, что зафиксированные данные никогда не будут потеряны. Это предотвращает потерю данных от сбоев питания или операционной системы, или других ошибок программного обеспечения.

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

  • Разделяемые блокировки:
    • При этой блокировке SQL Server позволяет другим сессиям выполнять операции для чтения блокированных данных. Однако препятствует обновлению пока блокировка активна.
    • Множество транзакций может накладывать разделяемую блокировку в одно и то же время на строку или страницу.
    • Это обычная блокировка, которую вы наблюдаете на объектах вашей базы данных.
    BEGIN TRAN 
    SELECT * FROM [SalesLT].[Customer] WITH (HOLDLOCK)
    WHERE CustomerID=1
    SELECT resource_type, request_mode, resource_description
    FROM sys.dm_tran_locks
    WHERE resource_type <> 'DATABASE'
    ROLLBACK

    Как показано ниже, имеется разделяемая блокировка на данном id ресурса (8194443284a0):

    • Эксклюзивные (X) блокировки:
      • SQL Server использует эксклюзивную блокировку (X-блокировка) для операций DML (Delete, Insert или Update), требующих модификацию строки или страницы данных.
      • Она предотвращает доступ других пользователей к ресурсу пока наложена блокировка.
      • SQL Server может может иметь только одну эксклюзивную блокировку на странице или строке в пределах транзакции.

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

    BEGIN TRAN 
    UPDATE [SalesLT].[Customer]
    SET Suffix='Mr.'
    WHERE CustomerID=1
    SELECT resource_type, request_mode, resource_description
    FROM sys.dm_tran_locks
    WHERE resource_type <> 'DATABASE'
    ROLLBACK

    • Блокировки обновления (U):
      • Блокировка обновления подобна эксклюзивной блокировке. Она может накладываться на запись, имеющую разделяемую блокировку.
      • Блокировка обновления накладывает другую разделяемую блокировку на конкретную строку. Как только записи смогут модифицироваться, SQL Server преобразует блокировку обновления в эксклюзивную блокировку.
      • SQL Server не может наложить разделяемую блокировку на ресурс с блокировкой обновления.
      • Вы можете также использовать WITH UPDLOCK для принудительной блокировки обновления.
      BEGIN TRAN 
      SELECT * FROM [SalesLT].[Customer] WITH (UPDLOCK)
      WHERE CustomerID=1
      SELECT resource_type, request_mode, resource_description
      FROM sys.dm_tran_locks
      WHERE resource_type <> 'DATABASE'
      ROLLBACK

      • Блокировки с намерением (Intent locks):
        • Их назначение — информировать транзакцию о намерении запросить блокировку. Это имеет место, когда транзакция требует разделяемую или эксклюзивную блокировку на ресурсы ниже в иерархии.
        • Транзакция не позволяет другим транзакциям получить эксклюзивную блокировку на таблицу, использующую блокировку с намерением.
        • Типы блокировок с намерением перечислены ниже.
          • Разделяемая блокировка с намерением (IS): указывает, что SQL Server намерен прочитать ресурсы ниже в иерархии, запрашивая разделяемую блокировку индивидуально на эти ниже находящиеся в иерархии ресурсы.
          • Эксклюзивная блокировка с намерением (IX): указывает, что SQL Server намерен модифицировать ресурсы ниже в иерархии, получая эксклюзивную блокировку на эти ниже находящиеся в иерархии ресурсы.
          • Блокировка обновления с намерением (IU): может запрашиваться только на уровне страницы для ресурсов ниже в иерархии, и по завершению обновления преобразуется в IX-блокировку.

      Как показано ниже, транзакция имеет эксклюзивную блокировку на ключе, и она имеет эксклюзивную блокировку с намерением на уровне страницы.

      Конверсионные блокировки

      • SIX — разделяемая с эксклюзивной блокировкой с намерением: Транзакция SQL Server удерживает разделяемую блокировку на нескольких страницах и имеет эксклюзивную блокировку на нескольких строках.
      • SIU — транзакция SQL Server удерживает разделяемую блокировку на нескольких страницах и имеет блокировку обновления на нескольких строках.
      • UIX — обновления с эксклюзивной блокировкой с намерением: транзакция SQL Server удерживает блокировку обновления на нескольких страницах и имеет эксклюзивную блокировку на нескольких строках.

      Блокировки схемы

      • Блокировка стабильности схемы (Sch-S): эта блокировка используется, когда схема в зависимости от запроса компилируется, и генерируется ее план выполнения. (Sch-S не накладывает никаких блокировок на данные объекта.
      • Блокировка модификации схемы (Sch-M): эта блокировка является результатом выполнения запроса DDL (язык определения данных). SQL Server может иметь только одну блокировку модификации схемы на объект. Вы не можете модифицировать объект при данной блокировке схемы.
      BEGIN TRAN 
      Alter TABLE DemoTable ADD new bit
      SELECT resource_type, request_mode, resource_description
      FROM sys.dm_tran_locks
      WHERE resource_type <> 'DATABASE'
      ROLLBACK

      Совместимость блокировок

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

      Эскалации блокировок

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

      • Порог памяти: пороговое значение блокировки памяти устанавливается на 40 процентах заблокированной памяти.
      • Порог блокировок: если число блокировок, установленных на текущей таблице или индексе больше 5000, может быть включена эскалация блокировок.
      ALTER TABLE Table_name SET (LOCK_ESCALATION = < TABLE | AUTO | DISABLE >–одна из этих опций) GO

      Обратитесь к документации Microsoft за подробной информацией об эскалации блокировок.

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

      Заключение

      Эта статья дает подробный обзор блокировок в SQL Server и DMV для мониторинга блокировки и процесса ее эскалации. Блокирование — это совершенно нормальное поведение в SQL Server, и вы должны быть знакомы с этим, чтобы понимать, как работают множественные транзакции.

      Обратные ссылки

      Нет обратных ссылок

      Комментарии

      Показывать комментарии Как список | Древовидной структурой

      Автор не разрешил комментировать эту запись

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

      Чтобы отслеживать блокировки, существующие в базе данных, можно выполнить запрос к динамическому административному представлению sys.dm_tran_locks . Это представление предоставляет строку для каждой отдельной блокировки, существующей в базе данных в настоящий момент.

      1. Запустите SQL Server Management Studio и откройте окно New Query (Новый запрос).
      2. Введите и выполните следующую инструкцию, чтобы запустить транзакцию и выполнить запрос к таблице Person.Contact . В этой транзакции для того, чтобы инструктировать SQL Server о том, что не надо освобождать блокировку после выполнения инструкции SELECT , используется подсказка блокировки HOLDLOCK . Обратите также внимание на то, что транзакция не фиксируется до тех пор, пока блокировки не будут автоматически освобождены с фиксацией. Как долго будет удерживаться блокировка в транзакции, объясняется позже в данной лекции. Код этого примера можно найти среди файлов примеров под именем MonitoringLocks.sql .
      USE AdventureWorks; GO BEGIN TRAN SELECT FirstName,LastName,EmailAddress FROM Person.Contact WITH (HOLDLOCK) WHERE ContactID = 15
      SELECT resource_type, resource_associated_entity_id, request_mode,request_status FROM sys.dm_tran_locks dml INNER JOIN sys.dm_tran_current_transaction dmt ON dml.request_owner_id = dmt.transaction_id; COMMIT TRAN
      BEGIN TRAN SELECT FirstName,LastName,EmailAddress FROM Person.Contact WITH (HOLDLOCK) WHERE ContactID 
      
      SELECT resource_type, resource_associated_entity_id,request_mode,request_status FROM sys.dm_tran_locks dml INNER JOIN sys.dm_tran_current_transaction dmt ON dml.request_owner_id = dmt.transaction_id; COMMIT TRAN

      Мы видим, что разделяемая блокировка определена для объектного типа ресурсов, в этом случае, для таблицы Person.Contact . SQL Server определяет, что для этой транзакции удержание блокировки на уровне таблицы будет проще и быстрее, чем удержание 7000 блокировок ключей со всеми зависимыми блокировками намерений. Поскольку SQL Server использовал уровень блокировки ТАБЛИЦА , ему не придется использовать блокировки намерений, ведь блокировка таблицы – самый верхний уровень в иерархии блокировок для данных. Чтобы узнать, какой объект блокируется, можно использовать функцию OBJECT_NAME . OBJECT_NAME в качестве аргумента принимает идентификатор объекта и возвращает имя объекта. (Столбец resource_associated_entity_id содержит идентификатор блокируемого объекта Object ID , если OBJECT представляет собой resource_type .)

      USE AdventureWorks; GO BEGIN TRAN UPDATE Person.Contact SET Phone ='+43 555 333 222' WHERE ContactID =25; SELECT resource_type, resource_associated_entity_id,request_mode,request_status FROM sys.dm_tran_locks dml INNER JOIN sys.dm_tran_current_transaction dmt ON dml.request_owner_id = dmt.transaction_id; ROLLBACK TRAN

      Результат показан на следующем рисунке. Мы видим, что SQL Server блокирует ключ при помощи монопольной блокировки ( request_mode = X ). При любом изменении данных SQL Server выполняет монопольную блокировку и удерживает ее до тех пор, пока не завершится транзакция. Как уже говорилось ранее, SQL Server также использует блокировки изменений на первом этапе выполнения инструкций UPDATE . Поскольку мы запросили информацию о блокировках после инструкции UPDATE , блокировка строки уже была повышена до монопольной блокировки . Мы видим также, что для страницы и таблицы снова существуют две блокировки монопольного намерения ( request_mode = IX ); кроме того, существует также блокировка под названием Sch-S для типа ресурсов METADATA . Блокировка Sch-S – это блокировка схемы, которая используется для того, чтобы запретить изменение схемы таблицы другими транзакциями в процессе обновления данных, потому что такие изменения не разрешаются в пр оцессе изменения данных.

      Уровень изоляции транзакции

      Вы убедились в том, что SQL Server изолирует транзакцию при помощи различных типов блокировок в блокируемых ресурсах. Чтобы разрабатывать надежные транзакции, важно не только определить содержание транзакции и случаи, в которых должен быть выполнен ее откат, но также и то, какие блокировки следует удерживать в процессе транзакции, и какую продолжительность они должны иметь. Это определяется через механизм уровней изоляции. Благодаря различным уровням изоляции SQL Server дает разработчикам возможность определить для каждой отдельной транзакции строгость изоляции от другой транзакции. Уровни изоляции транзакций определяют:

      • Будут ли блокировки использоваться при чтении данных
      • Как долго удерживаются блокировки
      • Какие типы блокировок используются для чтения данных
      • Что произойдет, если операции чтения потребуется считать данные, на которые распространяется монопольная блокировка другой транзакции. В этом случае SQL Server может:
      • Подождать до снятия блокировки другой транзакцией
      • Прочитать незафиксированные данные
      • Прочитать последнюю зафиксированную версию данных

      Стандарт ANSI 99 определяет четыре уровня изоляции транзакций; все они поддерживаются в SQL Server 2005:

      • READ UNCOMMITTED (чтение незафиксированных данных) не использует или не проверяет наличие блокировок при чтении данных. Следовательно, на этом уровне изоляции возможно чтение незафиксированных данных.

      Кроме того, в SQL Server есть два дополнительных уровня изоляции, которые используют для чтения данных версии строк (об этих уровнях изоляции мы подробно поговорим далее в этой лекции). Использование версий строк позволяет транзакции читать последнюю зафиксированную версию данных, если на эти данные распространяется монопольная блокировка . Это может способствовать существенному увеличению производительности запроса, поскольку операциям чтения не приходится ожидать снятия блокировок. Это такие два уровня, как:

      • READ COMMITTED SNAPSHOT (зафиксированное чтение, моментальный снимок) - это новая реализация уровня READ COMMITTED (зафиксированное чтение). В отличие от обычного уровня READ COMMITED , SQL Server читает последнюю версию зафиксированных данных, а, значит, при выполнении операций чтения ему не приходится ждать снятия блокировок. Этот уровень можно использовать вместо уровня READ COMMITED .
      • Уровень SNAPSHOT (моментальный снимок) использует для предоставления устойчивости транзакционного чтения управление версиями строк. Это означает, что в течение транзакции одни и те же данные всегда читаются с уровнем изоляции SERIALIZABLE , но при этом нет необходимости в блокировке данных для предотвращения изменения их другими транзакциями, поскольку благодаря управлению версиями строк обеспечивается согласованность чтения.

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

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

      Используем только чтение зафиксированных данных

      В SQL Server 2005 уровень изоляции READ COMMITED – это уровень изоляции по умолчанию при установлении нового соединения. Этот уровень существует в двух разновидностях: READ COMMITTED (чтение зафиксированных данных) и READ COMMITTED SNAPSHOT (чтение зафиксированных данных, мгновенный снимок). Применяемый тип определяется в параметрах базы данных. Уровень изоляции READ COMMITED ожидает снятия блокировок перед чтением данных, тогда как уровень изоляции READ COMMITTED SNAPSHOT использует управление версиями строк и читает последнюю версию зафиксированных данных, когда данные заблокированы другой транзакцией.

      Применяем уровень изоляции READ COMMITED
      1. Запустите SQL Server Management Studio и откройте окно New Query (Новый запрос).
      2. Введите и выполните следующие инструкции, чтобы прочитать поля Name и Email.Address в таблице Person.Contact . если ContactID = 1 . Код для этого примера включен в файлы примеров под именами ReadCommitted1.sql и ReadCommitted2.sql .
      USE AdventureWorks; BEGIN TRAN SELECT FirstName, LastName, EmailAddress FROM Person.Contact WHERE ContactID = 1;

      Возвращена строка из столбца Email.Address gustavo0@adventure-works.com , которая соответствует контактному лицу Gustavo Achong .

      USE AdventureWorks; BEGIN TRAN UPDATE Person.Contact SET EmailAddress = "uncommitted@email.at" WHERE ContactID = 1;
      SELECT FirstName, LastName, EmailAddress FROM Person.Contact WHERE ContactID = 1;

      Запрос не завершается, поскольку инструкция SELECT заблокирована. SQL Server пытается получить разделяемую блокировку на ключ столбца ContactID 1 , но это невозможно, ведь транзакция UPDATE в окне запроса Query Window 2 владеет монопольной блокировкой на этот ключ. Хотя окно запроса Query Windows 2 находится в режиме READ COMMITED (поскольку мы не изменяли уровень по умолчанию), монопольная блокировка все еще не снята. Эта блокировка существует потому, что монопольные блокировки для изменений данных всегда удерживаются до окончания транзакции.

      SELECT resource_type, resource_associated_entity_id, request_mode, request_status FROM sys.dm_tran_locks

      Мы видим, что одна из разделяемых блокировок имеет статус WAIT . Это запрос, выполняемый в окне Query Window 1. Он ожидает завершения запроса в окне Query Window 2, который владеет монопольной блокировкой, предоставленной на тот же ресурс.

      В этом примере мы увидели, что в режиме уровня изоляции READ COMMITED SQL Server ждет снятия монопольных блокировок, чтобы возвратить только актуальные и зафиксированные данные. Кроме того, нам стало ясно, что разделяемые блокировки удерживаются только на время чтения данных, а вот монопольные блокировки всегда удерживаются до фиксации транзакции . Такое поведение может вызвать проблемы, когда множество транзакций изменяют данные почти все время. В такой ситуации чтение данных может быть очень медленным из-за монопольных блокировок. Но в некоторых ситуациях может оказаться подходящим использование последней зафиксированной версии данных. В таких ситуациях можно изменить уровень изоляции READ COMMITTED на уровень изоляции READ COMMITTED SNAPSHOT .

      Исследуем блокировки в PostgreSQL

      Сегодня предлагаю вам вольный перевод весьма увлекательной и забавной статьи «Exploring Query Locks in Postgres».

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

      Песочница

      Для начала создадим «песочницу»:

      create database sandbox; create table toys ( id serial not null, name character varying(36), usage integer not null default 0, constraint toys_pkey primary key (id) ); insert into toys(name) values('car'),('digger'),('shovel'); 

      Откроем два терминала, в каждом из них подключимся к только что созданной базе данных sandbox. Чтобы не путаться, дадим им имена. Пусть это будут Алиса и Боб. Изменить подсказку командной строки можно с помощью команды \set :

      \set PROMPT1 '[Alice] %/> ' 

      Первой появляется Алиса и осматривает игрушки:

      [Alice] sandbox> begin; BEGIN [Alice] sandbox> select * from toys; id | name | usage ----+--------+------- 1 | car | 0 2 | digger | 0 3 | shovel | 0 (3 rows) 

      Обратите внимание, что оператор begin начинает транзакцию явно. В этом случае она будет продолжаться до тех пор, пока мы не зафиксируем её, сделаем commit , или не откатим, сделаем rollback .

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

      [Bob] sandbox> begin; BEGIN [Bob] sandbox> select * from toys; id | name | usage ----+--------+------- 1 | car | 0 2 | digger | 0 3 | shovel | 0 (3 rows) 

      Таким образом параллельное выполнение двух операторов select не мешает работе каждого из них. Именно такого поведения мы ожидаем от надёжной и высокопроизводительной базы данных.

      pg_lock

      Однако, транзакции Алисы и Боба до сих пор открыты. Чтобы посмотреть какие блокировки были установлены, откроем третий терминал и назовём его Ева:

      \set PROMPT1 '[Eve] %/> ' 
      select lock.locktype, lock.relation::regclass, lock.mode, lock.transactionid as tid, lock.virtualtransaction as vtid, lock.pid, lock.granted from pg_catalog.pg_locks lock left join pg_catalog.pg_database db on db.oid = lock.database where (db.datname = 'sandbox' or db.datname is null) and not lock.pid = pg_backend_pid() order by lock.pid; 
       locktype | relation | mode | tid | vtid | pid | granted ------------+-----------+-----------------+-----+-------+-------+--------- relation | toys_pkey | AccessShareLock | | 6/268 | 45265 | t relation | toys | AccessShareLock | | 6/268 | 45265 | t virtualxid | | ExclusiveLock | | 6/268 | 45265 | t relation | toys_pkey | AccessShareLock | | 1/282 | 45263 | t relation | toys | AccessShareLock | | 1/282 | 45263 | t virtualxid | | ExclusiveLock | | 1/282 | 45263 | t (6 rows) 

      Представление pg_lock показывает активные блокировки. Условие (db.datname = 'sandbox' or db.datname is null) оставляет только те записи которые относятся к «песочнице», а условие not pid = pg_backend_pid() исключает записи текущей сессии. Наконец, чтобы колонка relation стала более информативной было использовано приведение типа к regclass .

      Посмотрим на пятую строку:

       locktype | relation | mode | tid | vtid | pid | granted ------------+-----------+-----------------+-----+-------+-------+--------- relation | toys | AccessShareLock | | 1/282 | 45263 | t 

      Виртуальной транзакцией 1/282, на таблицу toys наложена блокировка AccessShareLock , при этом блокировка считается выданной (is granted). Пока всё идёт хорошо, Боб и Алиса счастливы, ведь они оба видят — игрушки можно взять.

      Обратите внимание, каждая транзакция удерживает блокировку ExclusiveLock на своей виртуальной транзакции virtualxid .

      Алиса решает взять машинку:

      [Alice] sandbox> update toys set usage = usage + 1 where 1 

      Никаких проблем. Посмотрим как выглядит таблица блокировок теперь:

       locktype | relation | mode | tid | vtid | pid | granted ---------------+-----------+------------------+----------+-------+-------+--------- relation | toys_pkey | AccessShareLock | | 6/268 | 45265 | t relation | toys | AccessShareLock | | 6/268 | 45265 | t virtualxid | | ExclusiveLock | | 6/268 | 45265 | t relation | toys_pkey | AccessShareLock | | 1/282 | 45263 | t relation | toys_pkey | RowExclusiveLock | | 1/282 | 45263 | t relation | toys | AccessShareLock | | 1/282 | 45263 | t relation | toys | RowExclusiveLock | | 1/282 | 45263 | t virtualxid | | ExclusiveLock | | 1/282 | 45263 | t transactionid | | ExclusiveLock | 24273800 | 1/282 | 45263 | t (9 rows) 

      transactionid

      В таблице toys на записи с машинкой теперь стоит блокировка RowExclusiveLock . Также появился реальный идентификатор транзакции transactionid на котором удерживается блокировка ExclusiveLock . Такой идентификатор появляется у каждой транзакции потенциально меняющей состояние базы данных.

      MVCC

      Поскольку транзакция Алисы не зафиксирована, Боб видит прежние данные:

      [Bob] sandbox> select * from toys; id | name | usage ----+--------+------- 1 | car | 0 2 | digger | 0 3 | shovel | 0 (3 rows) 

      Мы не знаем, будет ли Алиса фиксировать или откатывать свою транзакцию. Следовательно, Боб видит содержимое таблицы неизменным.

      Для того, чтобы каждый пользователь видел согласованное стостояние базы данных, постгрес использует механизм управления конкурентным доступом с помощью многоверсионности MVCC (Multi Version Concurrency Control).

      Блокирующие запросы

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

      [Bob] sandbox> update toys set usage = usage + 1 where >но ничего не происходит. Ему нужно подождать пока Алиса завершит свою транзакцию. Снова посмотрим в таблицу блокировок:
       locktype | relation | mode | tid | vtid | pid | granted ---------------+-----------+------------------+----------+-------+-------+--------- relation | toys_pkey | AccessShareLock | | 6/268 | 45265 | t relation | toys_pkey | RowExclusiveLock | | 6/268 | 45265 | t relation | toys | AccessShareLock | | 6/268 | 45265 | t relation | toys | RowExclusiveLock | | 6/268 | 45265 | t virtualxid | | ExclusiveLock | | 6/268 | 45265 | t relation | toys_pkey | AccessShareLock | | 1/282 | 45263 | t relation | toys_pkey | RowExclusiveLock | | 1/282 | 45263 | t relation | toys | AccessShareLock | | 1/282 | 45263 | t relation | toys | RowExclusiveLock | | 1/282 | 45263 | t virtualxid | | ExclusiveLock | | 1/282 | 45263 | t transactionid | | ExclusiveLock | 24273800 | 1/282 | 45263 | t tuple | toys | ExclusiveLock | | 6/268 | 45265 | t transactionid | | ExclusiveLock | 24273801 | 6/268 | 45265 | t transactionid | | ShareLock | 24273800 | 6/268 | 45265 | f (14 rows) 

      Теперь у Боба тоже есть transactionid и он просит выдать ему ShareLock на transactionid Алисы — «Мам, я тоже хочу поиграть машинкой». Поскольку две блокировки конфликтуют друг c другом, запрос Боба не удовлетворён (is not granted). Он будет висеть в таком состоянии до тех пор, пока Алиса не снимет ExclusiveLock , завершив свою транзакцию.

      pg_stats_activity

      pg_stat_activity ещё одно интересное представление (view) из pg_catalog’а. Оно показывает запросы выполняющиеся в данный момент:

      select query, state, waiting, pid from pg_stat_activity where datname = 'sandbox' and not (state = 'idle' or pid = pg_backend_pid()); 
       query | state | waiting | pid -------------------------------------------------+---------------------+---------+------- update toys set usage = usage + 1 where | active | t | 45265 update toys set usage = usage + 1 where | idle in transaction | f | 45263 (2 rows) 

      Мы видим, что запрос Алисы простаивает в ожидании поддтверждения транзакции (idle in transaction), в то время как запрос Боба активен и подвис (is waiting).

      Чтобы увидеть кто кого заблокировал, объединим два запроса в один:

      select bda.pid as blocked_pid, bda.query as blocked_query, bga.pid as blocking_pid, bga.query as blocking_query from pg_catalog.pg_locks bdl join pg_stat_activity bda on bda.pid = bdl.pid join pg_catalog.pg_locks bgl on bgl.pid != bdl.pid and bgl.transactionid = bdl.transactionid join pg_stat_activity bga on bga.pid = bgl.pid where not bdl.granted and bga.datname = 'sandbox'; 
       blocked_pid | blocked_query | blocking_pid | blocking_query -------------+-------------------------------------------------+--------------+------------------------------------------------- 45265 | update toys set usage = usage + 1 where | 45263 | update toys set usage = usage + 1 where row) 

      Если бы Алиса решила откатить или зафиксировать свою транзакцию, блокировка ExclusiveLock была бы снята и Боб получил бы ShareLock . После этого он мог бы зафиксировать свою транзакцию, и запись в таблице была бы обновлена независимо от решения Алисы.

      [Alice] sandbox> rollback; ROLLBACK [Bob] sandbox> commit; COMMIT [Bob] sandbox> select * from toys; id | name | usage ----+--------+------- 2 | digger | 0 3 | shovel | 0 1 | car | 1 (3 rows) 

      Конечно, если бы Боб и Алиса решили играть разными игрушками, конфликтной ситуации между ними не возникло бы вообще.

      Явные блокировки

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

      [Alice] sandbox> begin; BEGIN [Alice] sandbox> lock table toys in access exclusive mode; LOCK TABLE 

      Хотя Алиса и не взяла ни одной игрушки, Боб всё равно должен ждать.

      [Bob] sandbox> begin; update toys set usage = usage + 1 where >Таблица блокировок теперь выглядит так:
       locktype | relation | mode | tid | vtid | pid | granted ------------+----------+---------------------+-----+-------+-------+--------- virtualxid | | ExclusiveLock | | 6/284 | 45265 | t virtualxid | | ExclusiveLock | | 1/294 | 45263 | t relation | toys | RowExclusiveLock | | 6/284 | 45265 | f relation | toys | AccessExclusiveLock | | 1/294 | 45263 | t (4 rows) 

      Поскольку Алиса удерживает AccessExclusiveLock без изменения состояния базы данных, то она не получила свой transactionid . У Боба его тоже нет, потому что он не получил RowExclusiveLock на таблицу toys . В этой ситуации, запрос для отображения блокировок который мы использовали ранее, нам не поможет, т. к. он использует объединение по transactionid .

       blocked_pid | blocked_query | blocking_pid | blocking_query -------------+---------------+--------------+---------------- (0 rows) 

      Таким образом, Ева думает, что всё хорошо, в то время как следующий запрос:

      select pid, query, now() - query_start as waiting_duration from pg_catalog.pg_stat_activity where datname = 'sandbox' and waiting; 
       pid | query | waiting_duration -------+-------------------------------------------------+------------------ 35929 | update toys set usage = usage + 1 where | 00:01:34.519518 (1 row) 

      показывает обратное. Обратите внимание на столбец waiting_duration , он рассчитывается как разница между now() и query_start . Благодаря этому, видно сколько времени запрос уже висит.

      Сделав объединение по столбцам relation и locktype , мы снова можем видеть кто кого блокирует:

      select bgl.relation::regclass, bda.pid as blocked_pid, bda.query as blocked_query, bdl.mode as blocked_mode, bga.pid AS blocking_pid, bga.query as blocking_query, bgl.mode as blocking_mode from pg_catalog.pg_locks bdl join pg_stat_activity bda on bda.pid = bdl.pid join pg_catalog.pg_locks bgl on bdl.pid != bgl.pid and bgl.relation = bdl.relation and bgl.locktype = bdl.locktype join pg_stat_activity bga on bga.pid = bgl.pid where not bdl.granted and bga.datname = 'sandbox'; 
       relation | blocked_pid | blocked_query | blocked_mode | blocking_pid | blocking_query | blocking_mode ----------+-------------+-------------------------------------------------+------------------+--------------+-------------------------------------------+--------------------- toys | 35929 | update toys set usage = usage + 1 where | RowExclusiveLock | 35937 | lock table toys in access exclusive mode; | AccessExclusiveLock (1 row) 

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

      [Alice] sandbox> commit; COMMIT UPDATE 1 [Bob] sandbox> 

      Но его транзакция всё ещё открыта. Если мы посмотрим в таблицу блокировок, то увидим следующее:

       locktype | relation | mode | tid | vtid | pid | granted ---------------+-----------+------------------+-------+------+-------+--------- relation | toys_pkey | RowExclusiveLock | | 4/51 | 35929 | t virtualxid | | ExclusiveLock | | 4/51 | 35929 | t relation | toys | RowExclusiveLock | | 4/51 | 35929 | t transactionid | | ExclusiveLock | 19307 | 4/51 | 35929 | t (4 rows) 

      Лишь после того как Боб получил RowExclusiveLock , к его транзакции был добавлен transactionid . Боб рад и делает коммит:

      [Bob] sandbox> commit; COMMIT 

      RowExclusiveLock

      Поскольку Алиса не знает какую игрушку она хочет взять, а ставить явную блокировку ей не разрешили, она пробует другой подход:

      [Alice] sandbox> begin; select * from toys for update; BEGIN id | name | usage ----+--------+------- 2 | digger | 1 3 | shovel | 0 1 | car | 1 (3 rows) 

      На детском языке это бы звучало примерно так: «Хочу видеть все игрушки и может быть я возьму одну, но пока не знаю какую. А до тех пор я не хочу чтобы кто-то другой прикасался к ним».

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

      [Bob] sandbox> begin; update toys set usage = usage + 1 where >Ева видит следующую ситуацию:
       locktype | relation | mode | tid | vtid | pid | granted ---------------+-----------+------------------+-------+------+-------+--------- transactionid | | ShareLock | 19309 | 4/55 | 35929 | f relation | toys | RowExclusiveLock | | 4/55 | 35929 | t virtualxid | | ExclusiveLock | | 4/55 | 35929 | t transactionid | | ExclusiveLock | 19310 | 4/55 | 35929 | t tuple | toys | ExclusiveLock | | 4/55 | 35929 | t relation | toys_pkey | RowExclusiveLock | | 4/55 | 35929 | t relation | toys | RowShareLock | | 5/17 | 35937 | t virtualxid | | ExclusiveLock | | 5/17 | 35937 | t relation | toys_pkey | AccessShareLock | | 5/17 | 35937 | t transactionid | | ExclusiveLock | 19309 | 5/17 | 35937 | t (10 rows) 

      Боб совершенно ясно хочет изменить состояние базы данных поэтому он получил transactionid равный 19310, но снова вынужден ждать получения ShareLock на транзакцию Алисы с номером 19309.

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

      Пришло время объединить таблицу блокировок и таблицу активности вместе, так, чтобы всегда видеть кто кого заблокировал:

      select coalesce(bgl.relation::regclass::text, bgl.locktype) as locked_item, now() - bda.query_start as waiting_duration, bda.pid as blocked_pid, bda.query as blocked_query, bdl.mode as blocked_mode, bga.pid as blocking_pid, bga.query as blocking_query, bgl.mode as blocking_mode from pg_catalog.pg_locks bdl join pg_stat_activity bda on bda.pid = bdl.pid join pg_catalog.pg_locks bgl on bgl.pid != bdl.pid and (bgl.transactionid = bdl.transactionid or bgl.relation = bdl.relation and bgl.locktype = bdl.locktype) join pg_stat_activity bga on bga.pid = bgl.pid and bga.datid = bda.datid where not bdl.granted and bga.datname = current_database(); 
       locked_item | waiting_duration | blocked_pid | blocked_query | blocked_mode | blocking_pid | blocking_query | blocking_mode ---------------+------------------+-------------+-------------------------------------------------+--------------+--------------+--------------------------------+--------------- transactionid | 00:03:32.330397 | 35929 | update toys set usage = usage + 1 where | ShareLock | 35937 | select * from toys for update; | ExclusiveLock (1 row) 

      Для оценки времени блокирования запроса был добавлен столбец waiting_duration , а также функция current_database() используемая в условии.

      Ева не может запомнить этот длиннющий запрос и создаёт представление:

      create view lock_monitor as ( select coalesce(bgl.relation::regclass::text, bgl.locktype) as locked_item, now() - bda.query_start as waiting_duration, bda.pid as blocked_pid, bda.query as blocked_query, bdl.mode as blocked_mode, bga.pid as blocking_pid, bga.query as blocking_query, bgl.mode as blocking_mode from pg_catalog.pg_locks bdl join pg_stat_activity bda on bda.pid = bdl.pid join pg_catalog.pg_locks bgl on bgl.pid != bdl.pid and (bgl.transactionid = bdl.transactionid or bgl.relation = bdl.relation and bgl.locktype = bdl.locktype) join pg_stat_activity bga on bga.pid = bgl.pid and bga.datid = bda.datid where not bdl.granted and bga.datname = current_database() ); 

      С помощью него, она легко узнает что задумали её дети:

      [eve] sandbox> select * from lock_monitor; locked_item | waiting_duration | blocked_pid | blocked_query | blocked_mode | blocking_pid | blocking_query | blocking_mode ---------------+------------------+-------------+-------------------------------------------------+--------------+--------------+--------------------------------+--------------- transactionid | 00:06:19.986426 | 35929 | update toys set usage = usage + 1 where | ShareLock | 35937 | select * from toys for update; | ExclusiveLock (1 row) 

      Выпив чашку чая и успокоившись, Ева решает почитать руководство по явным блокировкам в постгресе, узнать какие бывают виды блокировок и то, как они конфликтуют друг с другом.

      "Гибкие" блокировки в 1С 7.7 и MS SQL Server

      Как-то на форуме прочитал сообщение типа" а вот знакомый админ удалил Tablockx и поставил rowlock в хранимых процедурах и все закрутилось, завертелось"… Эта мысль, по-моему, достаточно показательна для многих из 1С-программистов и особенно для новичков.

      Для того, чтобы не наломать дров в вашей ИТ системе, необходимо : во-первых, понимать, для чего существуют блокировки в МS SQL Server , во-вторых, понимать, как устроен блокировочный механизм в 1С 7.7 и MS SQL.

      По первой части есть масса литературы, и поэтому не хотелось бы ее пересказывать…Отмечу лишь принципиальные моменты…

      В MS SQL есть понятие блокировок и подсказок блокировок. Основное предназначение - избежать проблем некорректного(грязное чтение, чтение фантомов и т.п.) чтения информации.

      Для 1С 7.7 значимы следующие виды блокировок

      Holdlock – Захватывает разделяемую блокировку до завершения транзакции.

      Nolock - Применима только к оператору select . Читает все…

      Tablock - Используется блокировка на уровне таблиц.

      Tablockx - Используется монопольная блокировка таблицы.

      Отдельно выделю подсказки блокировки, которые «горячие» головы рекомендуют применять

      Rowlock - блокировка на уровне строк

      Updlock - блокировка на изменение

      Readpast - Применима только для Select . Пропускаются строки блокированные( rowlock ) другими транзакциями.

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

      На специфике реализации блокировок в 1С 7.7 остановлюсь подробнее.

      Механизм блокировок в 1С 7.7 максимально простой - блокируется все и по максимуму.

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

      Начнем с того, что в 1С 7.7 существует специальная таблица _1 sjourn, где хранятся внутренние идентификаторы всех документов. При записи, проведении и т.п. операциях с документами 1С накладывает блокировку на таблицу _1 sjourn и соответственно в системе в один момент времени может проводиться не более одного документа. То есть _1 sjourn выступает в роли своеобразного семафора. До тех пор, пока не завершиться транзакция и соответственно не будет снята блокировка с таблицы, все остальные клиенты будут ждать разблокировки и, самое интересное, как это они будут делать. В момент ожидания 1С 7.7 загружает ресурсы сервера, т.к. непрерывно сканирует таблицу на вопрос разблокировки и поэтому загружает процессор по максимуму.

      Удалить механизм блокировок можно путем изменения хранимых процедур, через которые 1С 7.7 проверяют таблицы на блокировки. А точнее, удалить хинты на блокировку таблиц.

      Например, в хранимой процедуре _1 sp __1 SJOURN _ TLockX в конструкции select @i=1 from _1SJOURN(TABLOCKX HOLDLOCK) where 0=1 необходимо удалить TABLOCKX HOLDLOCK.

      Также необходимо удалить соответствующие хинты(подсказки блокировки) в остальных таблицах, относящихся к документам. Если этого не сделать, то будут возникать deadlock -и – взаимоблокировки транзакций на уровне SQL Server .

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

      Создадим конфигурацию : справочник - Товары, документ – ПриходнаяНакладная, документ – РасходнаяНакладная, регистр – ОстаткиТовара Приходная накладная нужна только для того что бы сделать одно единственное движение. Больше логической нагрузки она нести не будет.

      В модуле расходной накладной реализуем следующую логику :

      Запрос = СоздатьОбъект("Запрос"); 
      ТекстЗапроса =
      "// |Товар = Регистр.Остатки.Товар;
      |Количество = Регистр.Остатки.Количество;
      |Функция КоличествоКонОст = КонОст(Количество);
      |Группировка Товар;
      |"//>>ЗАПРОС
      ;

      Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
      Возврат;
      КонецЕсли;

      Сообщить(НомерДок);
      Если Число(НомерДок)=1 Тогда
      Для Сч6=1 По 10000 Цикл
      Сообщить("Ждем второго. Вообще то здесь может идти
      | какой-то анализ или обработка данных.
      |Чем больше интервал между получением остатков и их
      | анализом с последующей записью движений -
      |тем больше вероятность возникновения отрицательных остатков");
      КонецЦИкла;
      КонецЕсли;


      Запрос.Группировка(1);
      Если (Запрос.КоличествоКонОст <0) Тогда
      Сообщить("Ошибка. ");
      Возврат;
      КонецЕсли;

      Если Запрос.КоличествоКонОст>=Количество Тогда
      Регистр.Остатки.Товар=Товар;
      Регистр.Остатки.Количество=Количество;
      Регистр.Остатки.ДвижениеРасходВыполнить();
      Иначе

      Сообщить("Не хватает!");
      Возврат;
      КонецЕсли;

      Товар это у меня реквизит шапки, и поэтому я использую одномерную (например использую Запрос.Группировка(1) зная что движение по одному товару) модель. Для многомерной модели ничего принципиально не меняется…

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

      • Первый пользователь получает остатки
      • Второй пользователь получает остатки
      • У первого пользователя идет какая либо обработка.(в моем случае выводится сообщение)
      • Второй пользователь проводит проверку остатков и списывает товар. Конец проведения.
      • Первый пользователь проводит анализ остатков и тут возникает самое интересное – информация по остаткам уже устарела! Но первый пользователь об этом не подозревает , проходит проверка остатка и списание. КонецПроведения.

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

      Как избежать этой неприятной ситуации? – Реализовать свой, гибкий механизм блокировок.

      Перем глСКЛ Экспорт;

      Процедура ПроверкаБлокировкиРесурса(Ресурс) Экспорт;
      глСКЛ.Закрыть();
      глСКЛ.Соединиться(0);
      //глСКЛ.ВыполнитьЗапрос("set lock_timeout 20000");
      Состояние("Проверка блокировки. ");
      Если Ресурс = "Регистр.ОстаткиТовара" Тогда
      Если (глСКЛ.ВыполнитьЗапрос("exec MyRA17_TLockX") = 1) тогда

      Иначе
      Сообщить("Ждет Занят MyRA17_TLockX");
      КонецЕсли;
      Если(глСКЛ.ВыполнитьЗапрос("exec MyRG17_TLockX") = 1) тогда

      Иначе
      Сообщить("Ждет Занят MyRG17_TLockX");
      КонецЕсли;
      КонецЕсли;
      глСКЛ.Закрыть();

      КонецПроцедуры

      В данном случае я блокирую по объекту метаданных Регистр.ОстаткиТовара.

      А хранимые процедуры MyRG17_TLockX и MyRA17_TLockX я полностью дублирую текстом старых RG17_TLockX и RA17_TLockX.

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

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

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

      Тут нужно во-первых понять, как хранит 1С данные и какими запросами получает данные.

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

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

      Для ответа на этот вопрос нужно во-первых понимать, что SQL Server сам расставляет блокировки объектов при изменении данных. Убедиться в этом можно, изменив в транзакции запись таблицы(как правило на индексированные) и запустив sp _ lock . По результатам sp _ lock мы увидим что данная запись заблокирована, как будто если бы мы в явном виде поставили подсказку rowlock .

      Вкратце концепция изменения агрегированных данных (например регистра остатков) 1С 7.7 следующая –

      При движении по регистру вызывается ряд хранимых процедур но показательные из них две это _1sp_GetNextPeriod - обеспечивает последовательное обновление агрегированных данных и _1sp_RG17_Change – собственно изменят конкретную запись. Для этого используется следующая конструкция:

      Update RG17 set SP19=Case When ABS(SP19+@p3)>9999999999 Then 9999999999 Else SP19+@p3 End where PERIOD=@per AND SP18=@p1 AND SP24=@p2 if @@ROWCOUNT=0 insert into RG17 values(@per,@p1,@p2,@p3)

      Как мы видим, в конструкции Update нет хинта rowlock . Но по большому счету он и не нужен т.к. сервер сам расставляет блокировки по измененным записям. В этом опять-таки не сложно убедиться, если провести ряд тестов, вкратце их смысл - искусственно создать ситуацию, при которой одна сессия будет изменять запись и другая ее будет изменять, но на основании прочитанных ранее устаревших данных. Вставка новых записей в агрегационную таблицу по документу происходит, как правило не более одной(если конечно точка актуальности не установлена абы как) и соответственно ситуация когда один документ делает вставку( insert into RG 17 values (@ per ,@ p 1,@ p 2,@ p 3) ) а другой ее не видит практически невозможна(кроме того, стоит ограничение на уровне уникальности индекса, для возможной повторной вставки).

      Теперь что касается грязного чтения. С этим дело обстоит хуже и к сожалению никакие установки rowlock в этом не помогут. Во-первых, информация по изменению должна пройти по всем агрегационным записям в пределах транзакции(хоть и маловероятно что в этот момент возникнет конфликт). Во-вторых, 1С в запросах использует хинт nolock , что вообще не оставляет шансов на создание универсального блокировочного механизма. Да и нужен ли он?

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

      Если же мы знаем, что для нас принципиальна информация по остаткам товара на складе, то соответственно ПроверкаБлокировкиОбъекта(« Склад ») . Таким образом, все документы по одному складу будут проводиться по очереди, а по разным складам параллельно. То же самое, если мы хотим сделать блокировки по Товару. Тогда будет соответственно ПроверкаБлокировкиСпискаОбъектов(спТовара) – но в этом случае нужно понимать, что чем больше информации по блокировкам, тем больше нагрузка на сервер. Необходимо найти золотую середину.

      Собственно, а как все вышеперечисленное реализовать. Вариантов несколько. Но раз мы уже остановились на классическом блокировочном механизме MS SQL, то можно воспользоваться процедурой sp_getapplock. Смысл ее в следующем – блокировка произвольного ресурса. Запустив sp _ lock мы можем убедиться, что это всего лишь еще одна запись. То есть например блокировку по складу(в контексте остатков по складу) можно сделать следующим образом sp_getapplock 'Остатки**Склад**Основной', 'Exclusive', 'transaction' . Соответственно аналогичная процедура выполненная из Сессии №2 сессии будет ждать завершения транзакции открытой Сессией №1 если в ней была запущенна sp_getapplock такими же параметрами. Процедура ПроверкаБлокировкиОбъекта всего лишь преобразует объект в уникальный идентификатор и передает его в качестве параметра в sp_getapplock в открытой сессии 1С 7.7.

      В конце статьи хочется отметить, что для того чтобы реализовать механизм «гибких» блокировок, необходимы какие-либо навыки в MSSQL Server, а также понимание внутренней структуры 1С 7.7. Например снимая хинты с процедуры _1sp__1SSYSTEM_TLock вы тем самим можете попасть в ситуацию когда два проведенных документа перемещающие ТА проведутся одновременно и один из них окажется позже точки актуальности или наоборот сняв хинты с _1sp__1SJOURN_TLock и не сняв их с _1sp_DT. _TLockX вы можете создать deadlock -и.

      При использовании материалов данной статьи обязательна ссылка на информационный ресурс.

      По интересующим Вас вопросам просьба обращаться на softpoint@softpoint.ru

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *