1с как посмотреть блокировки на sql
В данной статье я постараюсь воспроизвести все шаги анализа взаимоблокировки на реальном примере. В целом, действия при расследовании проблем могут отличаться от одного случая к другому, в данном примере я постараюсь применить разные инструменты разбора проблем.
Первоначальная задача и связанная с ней проблема
Начальные условия
Задача
Необходимо ускорить процесс переразнесения платежных поручений в десятки раз.
Методика решения
Проблема
Конфликт блокировок при выполнении транзакции:
Microsoft SQL Server Native Client 11.0: Transaction (Process ID 93) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
HRESULT=80004005, SQLSrvr: SQLSTATE=40001, state=34, Severity=D, native=1205, line=1
Анализ проблемы
Рассмотрим какие действия необходимо выполнить для анализа возникшей проблемы.
Технологический журнал
Первым делом необходимо настроить технологический журнал на сбор событий EXCP и DBMSSQL (последнее используется для получения исполняемых запросов на СУБД MS SQL Server). Для события EXCP достаточно установить только фильтр по имени базы, по этому событию мало пишется данных в технологический журнал. А вот событие DBMSSQL, наоборот, пишет очень много данных, поэтому здесь необходимо либо установить фильтры, либо разбирать огромный файл в поисках необходимой информации. Не буду останавливаться на этом вопросе подробно, скажу лишь то что обязательно должны писаться все события DBMSSQL с отбором по свойству lkp=1 (необходимо для получения информации о жертве конфликта блокировок на уровне СУБД). Если же захочется найти источника блокировки, тогда, необходимо будет писать хотя бы все запросы с отбором по таблице на которой происходит конфликт.
Поскольку для анализа проблемы я буду использовать отдельную базу, я включу для сбора все события EXCP и DBMSSQL с отбором по имени базы данных.
Далее воспроизводим проблемную ситуацию и смотрим результаты технологического журнала:
Структура базы данных. Таблица AccumRg17539
Поскольку совмещаемая блокировка не может конфликтовать с другой совмещаемой блокировкой, пытаемся в технологическом журнале найти конфликтующие запросы типа INSERT, DELETE или UPDATE с отбором по номеру соединения источника (для этого-то я и включил запись всех событий DBMSSQL). При этом учитываем что запрос жертвы должен был использовать индекс по регистратору и, значит, должен был установить совмещаемые блокировки только по своему регистратору. Поскольку в регистр накопления невозможно записать движения без отбора по регистратору, ищем запрос типа INSERT/DELETE/UPDATE по таблице _AccumRg17539 с отбором по номеру соединения источника и отбором по регистратору из текста SQL запроса жертвы (0x8E769C8E9919A5F011E6523D15F913A7). Но, к сожалению, подобного запроса у источника найти не удается, поэтому следующим шагом приходит мысль проверить точно ли запрос жертвы использует предполагаемый мной индекс.
План выполнения запроса
План запроса XML
Уже в плане запроса начинает смущать толстая стрелка исходящая из Index Seek, тем не менее, как видно, использован индекс по регистратору (_ByRecorder)
Параметры IndexSeek
Проверим предикаты поиска по индексу. Как видно, оператор Index Seek использует предикаты по типу регистратора и по ссылке, таким образом, кажется что запрос жертвы должен блокировать только записи по своему регистратору. При этом по технологическому журналу найти конфликтующий запрос не удалось.
Так же можно выполнить сам SQL запрос жертвы (из технологического журнала) в Management Studio и увидеть предполагаемый и актуальный планы запросов. В моем случае в актуальном плане запроса количество выбранных строк равно двум, в то время как в предполагаемом плане запроса количество строк ожидается 17230. Данный факт может говорить о неактуальной статистике, поэтому необходимо проверить эту версию. В моем случае статистика актуальна и, ожидаемо, обновление статистики и очистка процедурного кэша не дают никакого результата. При этом я запомню то что оптимизатор не смог правильно вычислить предполагаемое количество строк, хоть пока мне и не понятно что с этим делать.
Граф взаимоблокировки
Поскольку предыдущие шаги не дали результатов, получим граф взаимоблокировки. Для этого необходимо настроить Profiler на получение события Deadlock graph с отбором по исследуемой базе данных и выполнить действие еще раз. Во время выполнения операции, количество взаимоблокировок было не единично. Полученный список событий обширен, а графы разнообразны, но все они связаны с таблицей _AccumRg17539. При разборе взаимоблокировок необходимо начинать с самой простой, поэтому выберем самый простой граф.
Граф взаимоблокировки
Из графа уже можно понять проблему: процесс 92 установил IX блокировку на страницу индекса и процесс 84 установил IX блокировку на ту же страницу индекса. Блокировка IX совместима с другой блокировкой IX. Затем каждый из процессов запрашивает S блокировку (хочет конвертировать блокировку до уровня SIX) на ту же страницу индекса, но SIX блокировка не совместима с IX блокировкой, поэтому возникает взаимоблокировка и процесс 92 выбирается жертвой. Если посмотреть на запросы, то окажется что у обоих процессов S блокировку хотел установить запрос аналогичный вышеприведенному запросу жертвы из технологического журнала.
Метод расследования при помощи таймаута
Думаю каждый программист 1С работающий в более-менее крупной компании сталкивался с проблемой блокировки при проведении. Блокировка исчезает, как только ресурсы освободятся (проведется другой документ который блокировал регистр), но бывает что блокировка зависает надолго, а иногда очень надолго. Перезагружать сервер можно, но не много (до тех пор пока вас не выгонят с работы).
Необходимо локализовать проблему в коде и в структуре БД. Первое что приходит на ум - отладчик, но на самом деле это уже второй шаг, первым делом надо понять кто-что блокирует.
В случае SQL-сервером, это сделать наконец-то удалось. Приведенный ниже код SQL-запроса уже неоднократно помогал мне найти условного "виновника" (условного потому что виноват не пользователь, а программист который написал "кривой" или неоптимизированный код). Надеюсь поможет в работе многим. Код только частично мой, в основном был собран из разных источников с простора интернета, но к сожалению ссылок не сохранилось.
Использование
В повседневной работе достаточно задействовать только первый блок "/*кто кого*/". Открываем MS SQL Server Management Studio, правой на корень - "new query" - вставляем код запроса (только верхнюю часть до "Кто что блокирует", остальное - для детального анализа), выполняем запрос (вверху есть кнопочка выполнения запроса).
Выполнив запрос, запоминаем "ID виновника", быстренько идем в консоль сервера 1С, заходим в ветку "Сеансы" нашей базы. Видим все соединения с 1С-сервером, ищем там колонку "Соединение с СУБД", чтобы увидеть соединения 1С-сервера с MSSQL-сервером. В колонке будет всего несколько заполненных значений, среди них и будет "ID виновника".
Что делать если его там нет, тут 3 варианта:
1. Вернитесь в MSSQL и сделайте запрос еще несколько раз подряд, если значения меняются или таблица вообще пуста - постоянной блокировки нет, у вас (уже) все в порядке.
2. Сеанс который блокирует MSSQL находится в другой базе т.е. блокировка не в той базе (можно попробовать задействовать ветку всех сеансов в консоли 1С-сервера) - вернитесь в MSSQL и внимательно посмотрите в колонку DB в ней находится название базы.
3. Бывает что ID процесса в 1С-консоли вообще отсутствует, такое тоже бывает если у вас есть какие-то внешние программы подключенные напрямую в базу 1С, если пускаете кого-то в MSSQL напрямую, то вариант не исключен.
Всем привет! Очередная статья на тему «как мы улучшаем производительность». В данной статье я хочу показать, каким образом можно найти проблемы производительности и понять их причины, имея под рукой голую 1С, 2 руки и текстовый редактор.
0. Описание проблемы:
Итак, задача: наш сырный клиент, 1С:Молокозавод (на основе УПП 1.3), платформа 8.3.8, уровень изоляции транзакций Read committed snapshot . Множество ошибок превышения времени ожидания предоставления блокировки, взаимоблокировок. Особенно во время обменов данными по web-сервисам с некой WMS-системой. Клиент точно не может сказать, в какое время дня возникают проблемы. 1С:ЦУП отсутствует, к тому же, сбор данных ЦУП не желательно запускать более чем на 20 минут, т.к. замедляется работа исследуемой базы и сильно растёт файл данных для анализа, а мы не знаем, когда же нам поймать эти «20 минут». Поэтому мы будем использовать технологический журнал 1С.
1. Настройка технологического журнала
Для тех, кто не знает, на сервере 1С можно настроить логирование многих событий. Создаём специальный конфигурационный xml-файл, помещаем в папку conf сервера и сервер начинает запись логов. Подробнее останавливаться на этом не буду – в моём первом и втором «пятничном» вебинарах я подробно рассказывал о настройке технологического журнала 1С. Итак, включаю технологический журнал (далее – ТЖ) со следующими отборами: отбор по информационной базе, отбор по следующим событиям:
События TTIMEOUT и TDEADLOCK появились только в версии 8.3.7 платформы.
Итак, запускаем сбор логов, ставим ограничение, скажем, на 48 часов – платформа будет хранить логи за последние 48 часов.
2. Анализ логов технологического журнала
Через день я начал смотреть, что же у нас получилось:
С 8 до 9 утра было зафиксировано множество событий TDEADLOCK, TTIMEOUT, TLOCK, EXCP.
Пару слов о механизме управляемых блокировок:
С версии платформы 8.1 у нас появился режим управления блокировкой данных «управляемый»
К механизму блокировок СУБД добавляется механизм блокировок на сервере 1С. Блокировки мы можем ставить руками в коде с помощью объекта «БлокировкаДанных», также управляемая блокировка автоматически ставится при любой записи и при чтении данных в объектной технике, т.е. не запросом, а при получении каких-то данных в коде. Подробнее о механизме блокировок 1С можно я рассказывал здесь и здесь.
Теперь давайте разберем события ТЖ подробнее. EXCP:
События EXCP были следующего вида:
Ещё одно исключение для примера:
Я немного упростил лог. По этим строчкам видно, что возникали таймауты и дедлоки под пользователем АдминистраторWMS, в контексте отложенного проведения документов.
В нашей статье разберем взаимоблокировку. Смотрим событие TDEADLOCK:
Давайте разбирать этот жуткий текст. Взаимоблокировка, тот же пользователь АдминистраторWMS, далее идет информация о 2х блокировках, на которых и возник конфликт.
- 2896 2894 Seq31427.DIMS Exclusive Fld31428=79:a0b0000c6eb6752411db283f6c576d63
- 2896 – соединение, ожидающее блокировку
- 2894 – соединение, установившее блокировку
- SeqDIMS заблокированный ресурс. Имя таблицы начинается на Seq, значит это последовательность. Обработкой «Структура БД» по имени таблицы мы можем найти, какая именно последовательность.
- Exclusive – это тип блокировки «исключительная», такая ставится при записи
- Fld31428=79:a0b0000c6eb6752411db283f6c576d Сама блокировка на поле Fld31428. Это поле тоже можно «расшифровать в обработке «Структура БД»
- 2894 2896 AccRg14235.DIMS Exclusive
- Те же 2 сеанса, только наоборот. 2896 установил блокировку, 2894 – ожидает
- AccRgDIMS Exclusive. Эксклюзивная блокировка на таблице AccRg14235. Это РБ Хозрасчетный.
Итак, типичный случай взаимоблокировки «захват ресурсов в разном порядке». 1 сеанс заблокировал последовательность и ждет Хозрасчетный, второй сеанс заблокировал Хозрасчетный и ждет последовательность.
Теперь по номерам сеансов и датам событий мы найдем события TLOCK и посмотрим более детально, что происходило в каждом из этих сеансов.
Это 2 события TLOCK, на которых были ожидания блокировок. Мы видим наши номера сеансов connectID, а также WaitConnections – номера сеансов, которых ожидали. И контексты. В одном случае идет запись данных при проведении, а в другом – удаление записанных движений в начале проведения.
Проблему обнаружили. Теперь подумаем, что можно сделать для её решения и почему вообще она возникла.
Во-первых, и сеансы-виновники и сеансы-жертвы – это обмен с системой WMS. Таким образом получается, что обмен с WMS может проходить одновременно в несколько сеансов. Если по бизнес-логике в этом нет необходимости, можно исключить возможность параллельных обменов.
Во-вторых, блокировка при записи РБ Хозрасчетный. Даже если запись идет по одному набору измерений, ожиданий быть не должно, если включен режим разделения итогов.
Как видим, режим разделения итогов выключен. Необходимо включить.
И в-третьих, блокировка на последовательности при записи возможна только на одинаковом наборе измерений последовательности и если граница смещается при проведении. Находим нашу последовательность и смотрим свойства:
У данной последовательности 1 измерение «Организация». Соответственно, все документы, которые участвуют в последовательности, пытаются двигать её границу при проведении и ждут друг друга. Типовые последовательности двигают границу только назад, а вперед граница двигается специально обученной обработкой. В нашем случае необходимо анализировать логику работы и отказаться от оперативного перемещения границы, если есть такая возможность.
Остальные проблемы, а также настройку и работу с ТЖ смотрите на наших вебинарах.
Спасибо за внимание 🙂
Смотрите также:
Сегодня речь пойдет о блокировках данных в 1С. В видео использованы материалы статей: Материалы в рубрике Эксперт 1С Все видео из цикла «Записки эксперта»: Технологический журнал 1С (Записки эксперта…
Небольшая зарисовка на тему, какие были бы сливные трубы, если бы их делали 1С-ники.
В данном вебинаре я расскажу о применяемых в нашей компании правилах и приемах доработки типовых конфигураций 1С для облегчения их дальнейшей поддержки и обновления. В видео использованы материалы…
Продолжая тему динамических представлений MS SQL Server, в данной статье будет описано как получить информацию о текущих блокировках СУБД.
Эксперимент
Произведем те же действия что и в приведенной выше статье, а именно:
- Создадим тестовую базу и обработку
- Выполним запись в регистр сведений через обработку и встанем на ожидании, не завершив транзакцию
- В Management Studio выполним запрос выборки всех данных из таблицы регистра сведений
- Получим текущие исполняемые запросы в СУБД
Получение информации о текущих блокировках СУБД
Текущее состояние нашей системы дает возможность выполнить запрос, который вернет информацию о текущих блокировках СУБД. В моем запросе будут использованы следующие представления:
Представление | Описание |
---|---|
dm_tran_locks | Возвращает сведения об активных в данный момент в SQL Server ресурсах диспетчера блокировок |
partitions | Возвращает информацию о секциях |
indexes | Возвращает информацию об индексах |
Текст запроса приведен ниже, а также доступен во вложении к статье:
tl . resource_database_id = DB_ID ( 'MyBase' ) /* Condition by DB */ and p . object_id = object_id ( '_InfoRg243' ) /* Condition by table */Результат запроса к представлению dm_exec_requests (запрос из статьи, указанной в начале):
Текущие запросы СУБД
Результат запроса к представлению dm_tran_locks:
Запрошенные и установленные блокировки СУБД
Описания колонок результата запроса представлены ниже:
Имя колонки | Описание |
---|---|
DB_Name | Имя базы данных к которой выполняется запрос |
TableName | Имя сущности в базе данных, с которой связан ресурс (в примере ожидается имя таблицы) |
IndexID | Идентификатор индекса, связанного с таблицей |
IndexName | Имя индекса, связанного с таблицей |
ResourceType | Тип ресурса |
RequestMode | Запрашиваемый/предоставленный режим запроса |
RequestType | Тип запроса |
RequestStatus | Текущее состояние запроса |
RequestSessionID | Идентификатор сеанса, которому принадлежит этот запрос |
ResourceDescription | Описание ресурса |
Анализ полученного результата
Во-первых, интерпретируем результат запроса к представлению dm_tran_locks независимо от dm_exec_requests:
В результате данного запроса нет явного указания на связь заблокированного и блокирующего сеанса, хотя, при желании, сопоставив описания ресурсов, можно сделать вывод об этом. Поэтому, на мой взгляд, при разборе конфликтов блокировок логичнее сначала получить информацию по представлению dm_exec_requests, а затем уже получить информацию о том какие блокировки были установлены/запрошены источником и жертвой.
При таком подходе (сначала dm_exec_requests, затем dm_tran_locks) получим следующую интерпретацию:
Причина почему не возникает конфликта блокировок на уровне страниц индекса, несмотря на то что ситуация схожа с блокировкой на уровне ключей, заключается в том что блокировки IS и IX совместимы.
Читайте также: