Всем привет. Столкнулся с такой проблемой:

Изображение удалено.

Очень долго выполнялась бизнес-логика, когда пользователь CRM в странице редактирования Обращения пытается подгрузить шаблон письма. 

Запустил в SQL Profiler трасировку, выполнил действие, нашел.

Заметил, что Duration гораздо больше CPU + Reads, и нет никакого плана выполнения для такого запроса.

Сталкивался ли кто с подобной ситуацией? Какие меры предпринимали?

Я так понимаю, что рано или поздно данная ситуация возникает у всех, если развивать СРМ с куча интеграций и т.п., получения обращений в систему с 5 почт, подключением сотрудников в количестве от 30-48.

Ресурсов у сервера БД достаточно, 2 виртуалки, по 2 ТБ памяти, по 98 ГБ ОЗУ, процессор 2.4Ггц X 12 ядер.

Нравится

9 комментариев

Саид, как видно со скриншота, это запрос на вставку в таблицу активностей записи с названием "Диагностировать и решить инцидент #...".

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

Или дело в какой-то глобальной блокировке на таблицу, наложенную одновременнно работающей другой логикой.

Не пробовали аналогичный запрос (естественно, с другим Id) запускать из Management Studio вручную, так тоже тормозит?

Зверев Александр,

Да, это на добавление активности "Диагностировать  и решить инцидент ..." запись в трасировке, я забыл в описании указать. В следующий раз попробую через Management Studio выполнить запрос. Вот не знаю почему, но после добавления 2 индексов в SysCaseRight действия такие, как Отправка письма в обращении и т.д., переход между состояниями в кейсе страницы редактирования, стали работать быстрее заметно.

Саид, после добавления индексов ускорилась же не работа конкретного запроса вставки активности, а всё действие? Может, там перед этим (или после) ещё был длительный запрос чтения обращений с учётом прав, он и ускорился?

Зверев Александр,

Да, он ускорился.

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

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

Показать все комментарии

В рамках проекта 10% продукт Terrasoft 3.X был дополнен функционалом, который будет крайне необходим разработчикам: внутренний профайлинг запросов. Теперь запустив программу с ключем "profiler" можно увидеть все запросы (кроме запросов с паролем), которые DBEngine посылает серверу СУБД:

C:\Program Files\Terrasoft\Bin\TSClient.exe /profiler

2
Для того что бы использовать профайлер, Вы должны использовать бинарные файлы не ниже
  • 3.4.1.32
  • 3.4.0.118
  • 3.3.2.267

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

Вызвать журнал сообщений можно следующим образом
1
Или посмотреть txt-файл лога, который лежит в соответствующей папке. Пример для 3.4.0:

%appdata%\Terrasoft\3.4.0\Logs\

Встроенный профайлер позволяет:
  1. Полноценно разрабатывать конфигурацию пользователям, которые не являются системными администраторами СУБД, а только лишь dbowner-ами соответствующих БД (в MS SQL Server только системный администратор может работать с профайлером).
  2. Упростить разработку для FireBird, где профайлера нет вообще.

Напомню, что для SQL Server есть встроенная утилита SQL Server Profiler, видео пример использования:
SQL Profiler part 1 - Monitoring & Troubleshooting
SQL Profiler part 2 - Monitoring & Troubleshooting

Для Oracle, есть утилита от Quest Software: SQL Tracker, видео пример использования можно посмотреть пройдя по ссылке Quest Software - Toad for Oracle SQL Tracker

Нравится

Поделиться

5 комментариев

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

"Яворский Алексей" написал:В рамках проекта 10%

Что ж за проект такой если не секрет? :smile:
А за добавление спасибо!

Здравствуйте, Александр!
"Проект 10%" позволяет сотрудникам Terrasoft реализовывать собственные идеи в рамках рабочего времени.

Нашел интересную внешнюю статью про наш проект 10%

"Зверев Александр" написал:Интересно, запуск с профайлером сильно тормозит работу программы?

Теоретически конечно профайлер замедляет работу программы, ведь постоянно идет запись в файл на диске. Но на практики этого не заметно вовсе.
"Зверев Александр" написал:Было бы удобно в режиме разработчика включать его галкой в окне логина (аналогично сохранению пароля).

Нужно подумать. С одной стороны не плохо, а с другой -- много разных галочок не сильно хорошо :)

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

Показать все комментарии

Пишу кратко. Может кому-то следующая информация будет полезна и позволит сэкономить время.
Ниже я опишу как с помощью SQL Profiler я смог увидеть какие сервисы и в какой последовательности загружаются при запуске CRM с активным разделом - "Контрагенты". Также я смог получить информацию о времени потраченном при выполнении действий клиентской частью Terrasoft CRM и времени на выполнение запросов к БД.
Действия выполнялись на Terrasoft XRM 3.3.1.146 MS SQL.

Примечание: SQL Profiler-а нет в версии Express MS SQL Server

Шаги по подготовке:
1. CRM должна быть закрыта
2. Чистим папку с кэшем, чтобы увидеть все подтягиваемые сервисы из БД в профайлере.
Путь к папке для версии 3.3.1:
%appdata%\Terrasoft\3.3.1\Cache
3. Запускаем SQL Profiler

Получение трассировки запросов, выполняемых при загрузке CRM:
1. В SQL-Profiler создаем новую трассировку

2. Подключаемся к нашему серверу MS SQL

3. Запускаем трассировку со стандартными настройками:


4. Запускаем CRM и дожидаемся ее полной загрузки
5. Останавливаем трассировку
6. Сохраняем полученную трасировку в таблицу БД:

Подключаемся к базе и указываем базу и таблицу, в которую будут сохранены данные по трасировке.
Я использовал БД "master", владельца "dbo" и таблицу "tbl_trace1":

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

Очистка таблицы от мусора.
Далее, для очистки таблицы от ненужных для меня данных я выполнил следующие запросы на БД "master" в MS SQL Server Management Studio:
1. Удалил записи у которых TextData пустое, ИЛИ поле ApplicationName не равно "TSClient.exe", или Duration пустое, ИЛИ LoginName не равно вашему логину (в моем случае это был 'Supervisor'):

DELETE FROM tbl_trace1
WHERE TextData IS NULL OR Duration IS NULL OR ApplicationName > 'TSClient.exe' OR LoginName > 'Supervisor'


2. Удалил не нужные для анализа колонки - EventClass, ApplicationName, NTUserName, LoginName, ClientProcessID, SPID, BinaryData:

 

ALTER TABLE tbl_trace1
DROP COLUMN EventClass, ApplicationName, NTUserName, LoginName, ClientProcessID, SPID, BinaryData

 

 


Извлечение из таблицы трассировки полезной информации для дальнейшего анализа:
1. Добавляем колонки в таблицу:
DiffBetweenSteps - поле будет содержать значение времени в милисекундах, которое прошло между временем окончания выполнения предыдущего запроса и временем начала выполнения текущего запроса. Значение этого поля вмещает в себя время потраченное клиентской частью программы (выполнение скриптов, прорисовка окон и т.д. - все, что выполняется без обращения к серверу БД);
ServiceID и ServiceCode - поля содержащие ID и код сервиса, данные по которому подтягиваются из БД. По этим полям видно в какой последовательности выполняется загрузка сервисов во время запуска CRM;
PrevRowNumber - значение предыдущего по последовательности выполнения запроса, нужно для связки, если будет выполняться сортировка по какому-то полю.

ALTER TABLE tbl_trace1 ADD DiffBetweenSteps int, ServiceID uniqueidentifier, ServiceCode nvarchar(250), PrevRowNumber int


2. Создаем функцию для проверки того, что передаваемое как аргумент значение является GUID-значением и выполняем ее на базе данных "master" (в которой находиться наша таблица трассировки):

 

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
go

CREATE FUNCTION [dbo].[IsGuid] ( @testString varchar(38))
returns int
AS
begin
    declare @ret int
    SELECT  @ret = 0,
            @testString = REPLACE(REPLACE(@testString, '{', ''), '}', '')
    IF len(isnull(@testString, '')) = 36 AND
       @testString NOT LIKE '%[^0-9A-Fa-f-]%' AND
       -- check for proper positions of hyphens (-)  
       charindex('-', @testString) = 9 AND
       charindex('-', @testString, 10) = 14 AND
       charindex('-', @testString, 15) = 19 AND
       charindex('-', @testString, 20) = 24 AND
       charindex('-', @testString, 25) = 0
          SET @ret = 1
   
    RETURN @ret
end


Эта функция понадобиться в дальнейшем
3. Заполняем значение поля DiffBetweenSteps с помощью следующего sql кода:

 

 

-- Проставляем разницу во времени между окончанием одного запроса и началом выполнения следующего
declare @EndTimePrev datetime
declare @StartTimeNext datetime
declare @EndTimeNext datetime
declare @RowNumber int
declare @DiffBetweenSteps int

SET @EndTimePrev = NULL

declare c_Rec cursor FOR
  SELECT RowNumber, StartTime, EndTime FROM tbl_trace1

OPEN c_Rec
FETCH NEXT FROM c_Rec INTO @RowNumber, @StartTimeNext, @EndTimeNext
WHILE @@FETCH_STATUS=0
BEGIN
        SET @DiffBetweenSteps = DATEDIFF(millisecond, @EndTimePrev, @StartTimeNext)  

        IF (@DiffBetweenSteps IS NULL) SET @DiffBetweenSteps = 0

    UPDATE tbl_trace1
    SET DiffBetweenSteps = @DiffBetweenSteps  
    WHERE RowNumber = @RowNumber
   
    SET @EndTimePrev = @EndTimeNext

    FETCH NEXT FROM c_Rec INTO @RowNumber, @StartTimeNext, @EndTimeNext
END
CLOSE c_Rec
DEALLOCATE c_Rec


4. Заполняем полей ServiceID и ServiceCode с помощью следующего sql кода:

 

 

declare @RowNumber int
declare @ServiceGUID nchar(38)

-- Начиная с 205 позиции вырезаем 38 символов и сохраняем их в переменную @ServiceID
declare c_Rec cursor FOR
  SELECT RowNumber, substring(TextData, 205, 38) FROM tbl_trace1

OPEN c_Rec
FETCH NEXT FROM c_Rec INTO @RowNumber, @ServiceGUID
WHILE @@FETCH_STATUS=0
BEGIN
    -- Проверяем является ли вырезанный кусок текста GUID-значением
    IF (SELECT dbo.IsGuid(@ServiceGUID)) = 1
                BEGIN
                        -- Есди - да, то вытягиваем по полученому ID сервиса его код из рабочей базы CRM
                        UPDATE tbl_trace1
                        SET ServiceID = @ServiceGUID, ServiceCode = (SELECT Code FROM XRM.dbo.tbl_Service WHERE ID = @ServiceGUID)
                        WHERE RowNumber = @RowNumber           
                END
        ELSE
                BEGIN
                        UPDATE tbl_trace1
                        SET ServiceID = NULL, ServiceCode = ''
                        WHERE RowNumber = @RowNumber           
                END

    FETCH NEXT FROM c_Rec INTO @RowNumber, @ServiceGUID
END
CLOSE c_Rec
DEALLOCATE c_Rec

 

 

В коде

SELECT Code FROM XRM.dbo.tbl_Service WHERE ID = @ServiceGUID


вместо названия БД 'XRM' вам нужно указать название вашей рабочей CRM базы.

 

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

exec sp_executesql N'SELECT TOP 1
        [tbl_Service].[ID] AS [ID],
        [tbl_Service].[XMLData] AS [XMLData]
FROM
        [dbo].[tbl_Service] AS [tbl_Service]
WHERE([tbl_Service].[ID] = @P1)'
,N'@P1 nvarchar(38)',N'{64A6CED0-5C42-481F-97CA-C9632144B101}'


, где в конце указывается значение ID сервиса, то по этому ID мы можем получать необходимые данные о загружаемом сервисе, такие как код сервиса, его местонахождение в дереве сервисов и т.д. Для себя мы возьмем только код сервиса, чтобы наглядно было видно какой сервис запрашивается из БД.
5. Проставляем значение предыдущего шага в поле PrevRowNumber

 

-- Простановка предыдущего шага
declare @PrevRowNumber int
declare @RowNumber int

SET @PrevRowNumber = 0

declare c_Rec cursor FOR
  SELECT RowNumber FROM tbl_trace1

OPEN c_Rec
FETCH NEXT FROM c_Rec INTO @RowNumber
WHILE @@FETCH_STATUS=0
BEGIN
    UPDATE tbl_trace1
    SET PrevRowNUmber = @PrevRowNumber  
    WHERE RowNumber = @RowNumber
   
    SET @PrevRowNumber = @RowNumber

    FETCH NEXT FROM c_Rec INTO @RowNumber
END
CLOSE c_Rec
DEALLOCATE c_Rec


6. В итоге, с помощью SQL-запроса

 

 

SELECT RowNumber, TextData, CPU, Reads, Writes, Duration/(1000) AS Duration_t, StartTime, EndTime, ServiceCode, DiffBetweenSteps, PrevRowNumber
FROM tbl_trace1


выводим следующую таблицу:

7. Также можно выполнить сортировки по разным колонкам и увидеть:

 

 

 

SELECT RowNumber, TextData, CPU, Reads, Writes, Duration/(1000) AS Duration_t, StartTime, EndTime, ServiceCode, DiffBetweenSteps, PrevRowNumber
FROM tbl_trace1
ORDER BY CPU DESC

 

SELECT RowNumber, TextData, CPU, Reads, Writes, Duration/(1000) AS Duration_t, StartTime, EndTime, ServiceCode, DiffBetweenSteps, PrevRowNumber
FROM tbl_trace1
ORDER BY Duration_t DESC

 

SELECT RowNumber, TextData, CPU, Reads, Writes, Duration/(1000) AS Duration_t, StartTime, EndTime, ServiceCode, DiffBetweenSteps, PrevRowNumber
FROM tbl_trace1
ORDER BY DiffBetweenSteps DESC
  • каке запросы сколько используют процессорного времени в милисекундах на их выполнение

  • Выводимая таблица:

     

  • какие запросы дольше всего выполнялись, отсортировав по полю Duration. Значение в поле Duration храниться в микросекундах, делю на 1000, чтобы выводилось в милисекундах:

  • Выводимая таблица:

     

  • увидеть между какими запросами дольше всего происходили операции на стороне клиента, что поможет локализировать место в работе клиентской части Террасофт, которое требует больше всего времени на выполнение:

  • Выводимая таблица:

     

  • поля Reads и Writes показывают количество логических чтений и записей с жесткого диска на сервере во время выполнения события (запроса)

8. Также можно подсчитать общее время на выполнение запросов (ServerDuration), общее время на выполнение клиентской частью (ClientDuration), общее время на запуск CRM (OverallDuration):

SELECT Sum(Duration)/1000 AS ServerDuration, Sum(DiffBetweenSteps) AS ClientDuration, Sum(Duration/1000+DiffBetweenSteps) AS OverallDuration
FROM tbl_trace1


Выводимое значение:

 

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

Прикрепил SQL код, используемый мною - SLQ_code.txt
В архиве прикрепил картинки в их исходном размере - Images.zip.

Будет желание, делитесь полезными замечаниями :smile:

Нравится

Поделиться

0 комментариев
Показать все комментарии