Однократный доступ к датируемым триггерам - PullRequest
0 голосов
/ 06 декабря 2018

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

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

using (UnitOfWork unitofwork = new UnitOfWork())
{
    unitofwork.WebApiRequestLogRepository.Insert(new WebApiRequestLog
    {
        Created = DateTime.Now,
        Username = System.Threading.Thread.CurrentPrincipal.Identity.Name,
        Controller = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName,
        Method = actionContext.ActionDescriptor.ActionName,
        MethodParameters = xml,
        ApplicationVersion = AppSettings.Instance.Version,
        Url = HttpContext.Current.Request.Url.ToString(),
    });
    unitofwork.Save();
}

Unitofwork - это обертка вокруг одного контекста, в который можно вносить несколько изменений одновременно.Сохранение вызывает context.SaveChanges

Код SQL, сгенерированный структурой сущностей

INSERT [log].[WebApiRequestLog]([Created], [Username], [Controller], [Method], [ApplicationVersion], [Url], [MethodParameters])
VALUES (@0, @1, @2, @3, @4, @5, @6)
SELECT [Id]
FROM [log].[WebApiRequestLog]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()

Может кто-нибудь объяснить мне, почему одна вставка в таблицу журнала может вызвать тупик?

Редактировать

public virtual void Insert(TEntity entity)
{
    DbSet.Add(entity);
}

public void Save()
    {
        StringBuilder builder = new StringBuilder();
        try
        {
            _context.Database.Log = s => builder.AppendLine(s);
            _context.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            HandleDbEntityValidationException(e);
        }
        catch (Exception e)
        {
            Exception ex = new Exception(builder.ToString(), e);
            throw ex;
        }
    }

1 Ответ

0 голосов
/ 06 декабря 2018

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

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

  1. Убедитесь, что вы не нарушаетев TransactionScope или в том, что другой разработчик сидит на точке останова внутри блока кода TransactionScope.

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

    CREATE PROCEDURE SaveWebApiRequestLog(...)
    AS BEGIN
        SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
        INSERT log.WebApiRequestLog(Created,Username,Controller,Method,ApplicationVersion,Url,MethodParameters)
        VALUES(...)
        SELECT Id FROM log.WebApiRequestLog
        WHERE Id = @@IDENTITY
    END
    

    ИЛИ

    INSERT [log].[WebApiRequestLog]([Created], [Username], [Controller], [Method], [ApplicationVersion], [Url], [MethodParameters])
    VALUES (@0, @1, @2, @3, @4, @5, @6)
    SELECT [Id]
    FROM [log].[WebApiRequestLog] WITH NOLOCK
    WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
    

Показать ожидания

--kill 65
SELECT 
    S.Text, R.Session_id,R.Status,R.Command,R.CPU_Time,R.Total_Elapsed_Time
FROM    
    sys.dm_exec_requests R
    CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS S 

--EXEC sp_who2
SELECT  W.session_id,ot.task_state,W.wait_type, W.wait_duration_ms, W.blocking_session_id, W.resource_description, S.host_name, S.program_name
FROM  
    sys.dm_os_waiting_tasks  W  
    INNER  JOIN sys.dm_os_tasks ot ON ot.task_address = W.waiting_task_address 
    INNER JOIN sys.dm_exec_sessions S ON S.session_id = W.session_id 
WHERE 
    S.is_user_process =  1

SELECT 
    SessionID = es.session_id,
    Login =es.original_login_name,
    Host = es.host_name,
    RequestStart=es.last_request_start_time,
    RequerstEnd=es.last_request_end_time,
    Status =es.status,
    BockedBy =er.blocking_session_id,
    WaitType =er.wait_type, 
    WaitTime = er.wait_time,
    LastWaitType = er.last_wait_type,
    WaitResource = er.wait_resource,
    DatabaseID = DB_NAME(er.database_id),
    Command = er.command,
    --,sql_text=st.text
    TransactionIsolation =
        CASE es.transaction_isolation_level
            WHEN 0 THEN 'Unspecified'
            WHEN 1 THEN 'Read Uncommitted'
            WHEN 2 THEN 'Read Committed'
            WHEN 3 THEN 'Repeatable'
            WHEN 4 THEN 'Serializable'
            WHEN 5 THEN 'Snapshot'
        END,
    CPUTime = COALESCE(es.cpu_time,0) + COALESCE(er.cpu_time,0),
    IOTime = COALESCE(es.reads,0) + COALESCE(es.writes,0) + COALESCE(er.reads,0) + COALESCE(er.writes,0),
    TransactioCount = COALESCE(er.open_transaction_count,-1),
    ProgramName = COALESCE(es.program_name,''),
    LoginTme = es.login_time
FROM 
    sys.dm_exec_sessions es
    LEFT OUTER JOIN sys.dm_exec_connections ec ON es.session_id = ec.session_id
    LEFT OUTER JOIN sys.dm_exec_requests er ON es.session_id = er.session_id
    LEFT OUTER JOIN sys.server_principals sp ON es.security_id = sp.sid
    LEFT OUTER JOIN sys.dm_os_tasks ota ON es.session_id = ota.session_id
    LEFT OUTER JOIN sys.dm_os_threads oth ON ota.worker_address = oth.worker_address
    CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS st
WHERE 
    es.is_user_process = 1 
    and 
    es.session_id <> @@spid
ORDER BY
     es.session_id
...