【发布时间】:2014-09-27 11:54:05
【问题描述】:
注意:当问题没有正确处理阅读器/连接,或者错误是由于处理不当的延迟加载时,我已经经历了数百万个问题。我相信这个问题是另一个问题,可能与 MySQL 的 .NET 连接器有关。
我通过其 .NET 连接器 (6.8.3) 广泛使用 MySQL 服务器 (5.6) 数据库。出于性能原因,所有表都是使用 MyISAM 引擎创建的。我只有 一个进程 与 一个线程 (update: 事实上,这不是真的,见下文)访问数据库是顺序的,所以不需要事务和并发。
今天,在处理了以下一段代码之后:
public IEnumerable<VectorTransition> FindWithSourceVector(double[] sourceVector)
{
var sqlConnection = this.connectionPool.Take();
this.selectWithSourceVectorCommand.Connection = sqlConnection;
this.selectWithSourceVectorCommand.Parameters["@epsilon"].Value
= this.epsilonEstimator.Epsilon.Min() / 10;
for (int d = 0; d < this.dimensionality; ++d)
{
this.selectWithSourceVectorCommand.Parameters["@source_" + d.ToString()]
.Value = sourceVector[d];
}
// *** the following line (201) throws the exception presented below
using (var reader = this.selectWithSourceVectorCommand.ExecuteReader())
{
while (reader.Read())
{
yield return ReaderToVectorTransition(reader);
}
}
this.connectionPool.Putback(sqlConnection);
}
抛出以下异常:
MySqlException: 已经有一个打开的 DataReader 与此 Connection 关联,必须先关闭。
这是堆栈跟踪的相关部分:
在 MySql.Data.MySqlClient.ExceptionInterceptor.Throw(Exception 异常) 在 MySql.Data.MySqlClient.MySqlConnection.Throw(异常前) 在 MySql.Data.MySqlClient.MySqlCommand.CheckState() 在 MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior 行为) 在 MySql.Data.MySqlClient.MySqlCommand.ExecuteReader() 在 implementation.VectorTransitionsMySqlTable.d__27.MoveNext() 在 C:\Users\bartoszp...\implementation\VectorTransitionsMySqlTable.cs:line 201
在 System.Linq.Enumerable.d__3a
1.MoveNext() at System.Linq.Buffer1..ctor(IEnumerable1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable1 源) 在 implementation.VectorTransitionService.Add(VectorTransition vectorTransition) 在 C:\Users\bartoszp...\implementation\VectorTransitionService.cs:line 38在 Program.Go[T](Environment`2 p, Space parentSpace, EpsilonEstimator epsilonEstimator, ThresholdEstimator thresholdEstimator, TransitionTransformer transitionTransformer, AmbiguityCalculator ac, VectorTransitionsTableFactory vttf, AxesTableFactory atf, NeighbourhoodsTableFactory ntf, AmbiguitySamplesTableFactory astf, AmbiguitySampleMatchesTableFactory asmtf, MySql rejectDuplicates, Boolean addNew) 在 C:\Users\bartoszp...\Program.cs:line 323
connectionPool.Take 返回满足以下谓词的第一个连接:
private bool IsAvailable(MySqlConnection connection)
{
var result = false;
try
{
if (connection != null
&& connection.State == System.Data.ConnectionState.Open)
{
result = connection.Ping();
}
}
catch (Exception e)
{
Console.WriteLine("Ping exception: " + e.Message);
}
return result && connection.State == System.Data.ConnectionState.Open;
}
(这与我之前的问题有关,当时我解决了一个不同但类似的问题:MySQL fatal error during information_schema query (software caused connection abort))
FindWithSourceVector 方法由以下代码调用:
var existing
= this.vectorTransitionsTable
.FindWithSourceVector(vectorTransition.SourceVector)
.Take(2)
.ToArray();
(我最多需要找到两个重复的向量) - 这是堆栈跟踪的 VectorTransitionService.cs:line 38 部分。
现在最有趣的部分:当调试器在异常发生后停止执行时,我调查了sqlConnection 对象发现,它没有与之关联的阅读器(下图)!
为什么会发生这种情况(显然是“随机的”——在过去的 ~20 小时内几乎每分钟都会调用此方法)?我可以避免这种情况(以其他方式猜测 - 当Ping 抛出异常并祈祷它会有所帮助时添加一些睡眠)?
关于连接池实现的附加信息:
Get 适用于仅调用简单查询且不使用读取器的方法,因此返回的连接可以以可重入的方式使用。本例中没有直接使用(因为涉及到读者):
public MySqlConnection Get()
{
var result = this.connections.FirstOrDefault(IsAvailable);
if (result == null)
{
Reconnect();
result = this.connections.FirstOrDefault(IsAvailable);
}
return result;
}
Reconnect 方法只是遍历整个数组并重新创建和打开连接。
Take 使用Get,但也会从可用连接列表中删除返回的连接,因此如果某些方法在使用阅读器期间调用其他也需要连接的方法,则不会共享该连接。这里也不是这种情况,因为FindSourceVector 方法很简单(不调用使用数据库的其他方法)。但是,为了约定,使用Take - 如果有读者,请使用Take:
public MySqlConnection Take()
{
var result = this.Get();
var index = Array.IndexOf(this.connections, result);
this.connections[index] = null;
return result;
}
Putback 只是将连接放置到第一个空点,或者如果连接池已满,则忽略它:
public void Putback(MySqlConnection mySqlConnection)
{
int index = Array.IndexOf(this.connections, null);
if (index >= 0)
{
this.connections[index] = mySqlConnection;
}
else if (mySqlConnection != null)
{
mySqlConnection.Close();
mySqlConnection.Dispose();
}
}
【问题讨论】:
-
你还没有展示你对方法的返回值做了什么。考虑到迭代器块的执行方式,肯定有一些方法可以让你最终不关闭阅读器。
-
@JonSkeet 感谢您的宝贵时间。我已添加通话。
-
这是多线程吗?也许一个计时器和过程花费的时间比间隔时间长?似乎是某种竞争条件。
-
@TyCobb 不,正如我所说,它只是一个线程,目前没有其他进程正在使用这个数据库。感谢您对此进行调查,这对我来说非常重要:)
-
@BartoszKP:即使我的回答有效,您也应该绝对回到正常的连接池。这么说吧:每天都有数以百万计的程序使用内置的连接池。与连接池代码中的错误相比,它更有可能是代码中的错误,因此添加 more 代码很可能会添加更多错误......看起来就是这样在这里。
标签: c# mysql .net database datareader