【问题标题】:MySQL - DBUpdateException ('Deadlock found when trying to get lock; try restarting transaction')MySQL - DBUpdateException('尝试获取锁时发现死锁;尝试重新启动事务')
【发布时间】:2019-01-13 00:45:02
【问题描述】:

我在 .NET Core 中有一个使用 MySQL 的 mvc 应用程序。我有一个下载功能,其中传递了 id 来下载文件。但是,错误不断发生,并且似乎以某种方式该应用程序在服务器上的响应速度变慢,就好像它正在被回收一样。以下代码显示了负责下载文件的部分。我正在使用 TransactionScope 来确保下载计数器已正确更新。我得到的错误与死锁有关。如果有人能指出问题出在哪里,是否有可能?

using (var context = new MyDbContext())
using (TransactionScope scope = new TransactionScope())
{
    ProjectDownload download = context.ProjectDownload.Include(pd => pd.Project).Where(d => d.Id == downloadId).FirstOrDefault();

    if (download == null) throw new InvalidOperationException("Cannot find ProjectDownload.Id");

   download.DownloadCounter++;
   download.Project.DownloadCounter++;

   try
   {
       context.SaveChanges();
       scope.Complete();
       return (ProjectDownloadModel)download;
   }
   catch (DbUpdateException ex){
       throw;
   }
}

也试过这种方式将之前的代码改为:

using (var context = new MyDbContext())
using (TransactionScope scope = new TransactionScope())
{
    ProjectDownload download = context.ProjectDownload.Where(d => d.Id == downloadId).FirstOrDefault();

   download.DownloadCounter++;

   Project proj = context.Project.Where(p => p.ProjectDownload.Any(pd => pd.Id == downloadId)).FirstOrDefault();
   proj.DownloadCounter++;
   ...

出现以下错误:

Exception has occurred: CLR/MySql.Data.MySqlClient.MySqlException
An exception of type 'MySql.Data.MySqlClient.MySqlException' occurred in Microsoft.EntityFrameworkCore.dll but was not handled in user code: 'Deadlock found when trying to get lock; try restarting transaction'
  at MySql.Data.MySqlClient.MySqlStream.ReadPacket()
  at MySql.Data.MySqlClient.NativeDriver.GetResult(Int32& affectedRow, Int64& insertedId)
  at MySql.Data.MySqlClient.Driver.NextResult(Int32 statementId, Boolean force)
  at MySql.Data.MySqlClient.MySqlDataReader.NextResult()
  at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
  at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues)
  at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)    
  at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)    
  at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
  at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()    
  at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found)    
  at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ResultEnumerable`1.GetEnumerator()
  at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.<_TrackEntities>d__17`2.MoveNext()
  at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
  at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found)    
  at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)    
  at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass15_1`1.<CompileQueryCore>b__0(QueryContext qc)    
  at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)    
  at ProjectProcess.DownloadProject(Int32 downloadId) in C:\Users\Projects\ProjectWebsite\BusinessLogic\ProjectProcess.cs:line 124    
  at ProjectController.DownloadProject(Int32 downloadID) in C:\Users\Projects\ProjectWebsite\Controllers\ProjectController.cs:line 33    
  at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)    
  at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeActionMethodAsync>d__12.MoveNext()

我不明白死锁是如何发生的以及如何解决它

【问题讨论】:

标签: c# mysql entity-framework-core transactionscope


【解决方案1】:

问题是您没有锁定某些记录。在mysql中你可以像这样锁定行

SELECT * FROM users WHERE name = 'name' FOR UPDATE;

但是对于你的问题,你可以试试这个。希望能解决你的问题

using (var scope = new TransactionScope(TransactionScopeOption.RequireNew,
new TransactionOptions { 
    IsolationLevel = IsolationLevel.ReadUncommitted 
}))
{
  // your select and increment should go here  
}

【讨论】:

  • 谢谢。现在我收到另一个错误连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立连接失败,因为连接的主机在 System.Net.Sockets.NetworkStream 上没有响应\r\n。 Read(Byte[] buffer, Int32 offset, Int32 size)} Message [string]:"无法从传输连接中读取数据:连接尝试失败,因为连接方在一段时间后没有正确响应,或建立连接失败,因为连接的主机没有响应。”
  • 一个问题,为什么有一个默认的 TransactionScope 还不够?
  • 谢谢@mjwills 我想现在我知道 Serializable 是默认的隔离级别,这是最安全的级别,但并发能力最低。但无论如何,为什么我的查询中出现死锁?
  • 可能Serializable 隔离级别是您死锁的原因。
  • 可能是因为您缺少 finally 范围回滚部分。你从中间抛出异常。用try覆盖全身,最后添加
【解决方案2】:

你能再尝试一件事吗?快速修复

//replace your line with this
if (download == null) {
    context.SaveChanges();
    scope.Complete();
    throw new InvalidOperationException("Cannot find ProjectDownload.Id");
}

【讨论】:

  • 感谢@TalhaAkber。我会补充一点。顺便说一句,重新启动 MySQL 服务后一切似乎都很好。我不确定问题是什么。无论如何,我会继续检查。
  • 使用默认的 IsolationLevel 还是您更改了 TransactionScope ?主要原因是您开始交易但从未完成,因为您从中间抛出异常。在尝试捕获并回滚您的范围后尝试考虑 finally 标记
  • 使用默认的 IsolocationLevel 会引发异常。
【解决方案3】:

尽量把这些放在使用Transaction Scope之前

ProjectDownload 下载 = context.ProjectDownload.Where(d => d.Id == downloadId).FirstOrDefault(); 项目 proj = context.Project.Where(p => p.ProjectDownload.Any(pd => pd.Id == downloadId)).FirstOrDefault();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-18
    • 2011-01-20
    • 2013-09-20
    • 2011-02-05
    • 1970-01-01
    • 2020-10-08
    • 2016-05-13
    相关资源
    最近更新 更多