单项数据表触发死锁
Single entry to datatable triggers deadlock
我们有一个 webapi,它处理大量请求,有时每秒处理一次,在每个请求开始时,我们使用 Entity framework.[=13 将日志条目发送到我们的日志table =]
但是,我们日志中的这个条目table通常会在另一个进程上发生死锁,我不明白是什么原因造成的。
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();
}
工作单元是围绕单个上下文的包装器,可以同时进行多项更改。保存调用 context.SaveChanges
SQL 由 entity framework
生成的代码
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()
任何人都可以向我解释为什么单次插入日志 table 会导致死锁吗?
编辑
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;
}
}
我抛出一些有用的查询,您可以在死锁期间 运行 显示当前等待。它们可以帮助您识别死锁的情况。
此外,您可能想查找如何 save deadlock graphs to the database when they occur. If you posted a deadlock graph over at Database Administrator Exchange,我相信您会找到有关什么是锁定的更多信息。
确保您没有中断 TransactionScope 或 另一个开发人员正处于 TransactionScope
代码块内的断点。
如果您的数据库非常繁忙并且您不担心脏,那么您可以使用以下提示减少读锁。
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
我们有一个 webapi,它处理大量请求,有时每秒处理一次,在每个请求开始时,我们使用 Entity framework.[=13 将日志条目发送到我们的日志table =]
但是,我们日志中的这个条目table通常会在另一个进程上发生死锁,我不明白是什么原因造成的。
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();
}
工作单元是围绕单个上下文的包装器,可以同时进行多项更改。保存调用 context.SaveChanges
SQL 由 entity framework
生成的代码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()
任何人都可以向我解释为什么单次插入日志 table 会导致死锁吗?
编辑
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;
}
}
我抛出一些有用的查询,您可以在死锁期间 运行 显示当前等待。它们可以帮助您识别死锁的情况。
此外,您可能想查找如何 save deadlock graphs to the database when they occur. If you posted a deadlock graph over at Database Administrator Exchange,我相信您会找到有关什么是锁定的更多信息。
确保您没有中断 TransactionScope 或 另一个开发人员正处于
TransactionScope
代码块内的断点。如果您的数据库非常繁忙并且您不担心脏,那么您可以使用以下提示减少读锁。
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