Bar*_*zKP 6 .net c# mysql database datareader
注意:当问题没有正确处理读取器/连接时,或者错误是由于处理不当的延迟加载时,我已经经历了数百万个问题.我相信这个问题是另一个问题,可能与MySQL的.NET连接器有关.
我通过其.NET连接器(6.8.3)广泛使用MySQL服务器(5.6)数据库.出于性能原因,所有表都是使用MyISAM引擎创建的.我只有一个进程有一个线程(更新:事实上,它不是真的,见下文)顺序访问数据库,因此不需要事务和并发.
今天,经过几个小时的处理,下面这段代码:
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);
}
Run Code Online (Sandbox Code Playgroud)
抛出以下异常:
MySqlException:已经有一个与此Connection关联的打开DataReader,必须先关闭它.
这是堆栈跟踪的相关部分:
在MySql.Data.MySqlClient.ExceptionInterceptor.Throw(例外的例外)在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:\用户\ bartoszp ... \实施\ VectorTransitionsMySqlTable.cs:线201
在System.Linq.Enumerable.d__3a
1.MoveNext() at System.Linq.Buffer1..ctor(IEnumerable的1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable1个源)在implementation.VectorTransitionService.Add(VectorTransition vectorTransition)在C:\用户\ bartoszp ... \实施\ VectorTransitionService.cs:线38在Program.Go [T](Environment`2 P,空间parentSpace,EpsilonEstimator epsilonEstimator,ThresholdEstimator thresholdEstimator,TransitionTransformer transitionTransformer,AmbiguityCalculator AC,VectorTransitionsTableFactory VTTF,AxesTableFactory ATF,NeighbourhoodsTableFactory NTF,AmbiguitySamplesTableFactory ASTF,AmbiguitySampleMatchesTableFactory asmtf,MySqlConnectionPool连接池,布尔rejectDuplicates,布尔addNew)在C:\ Users\bartoszp ...\Program.cs:第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;
}
Run Code Online (Sandbox Code Playgroud)
(这与我之前的问题有关,当我解决了一个不同但相似的问题:在information_schema查询期间发生MySQL致命错误(软件导致连接中止))
该FindWithSourceVector方法由以下代码调用:
var existing
= this.vectorTransitionsTable
.FindWithSourceVector(vectorTransition.SourceVector)
.Take(2)
.ToArray();
Run Code Online (Sandbox Code Playgroud)
(我需要找到最多两个重复的向量) - 这是堆栈跟踪的VectorTransitionService.cs:第38行的一部分.
现在最有趣的部分是:当调试器在发生异常后停止执行时,我已经调查了sqlConnection要查找的对象,它没有与之关联的阅读器(下图)!

为什么会发生这种情况(显然是"随机" - 这种方法几乎每隔一分钟被调用一次,直到最后~20h)?我可以避免这种情况(以其他方式猜测 - 在Ping抛出异常并祈祷它会帮助时添加一些睡眠)?
有关连接池实现的其他信息:
Get适用于仅调用简单查询且不使用读取器的方法,因此可以以可重入的方式使用返回的连接.它不直接在此示例中使用(因为涉及读者):
public MySqlConnection Get()
{
var result = this.connections.FirstOrDefault(IsAvailable);
if (result == null)
{
Reconnect();
result = this.connections.FirstOrDefault(IsAvailable);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
该Reconnect方法只是迭代整个数组并重新创建并打开连接.
Take使用Get但也从可用连接列表中删除返回的连接,因此,如果某些方法在使用读取器期间调用其他需要连接的方法,则不会共享它.这也不是这种情况,因为FindSourceVector方法很简单(不会调用其他使用DB的方法).但是,Take为了惯例而使用 - 如果有读者,请使用Take:
public MySqlConnection Take()
{
var result = this.Get();
var index = Array.IndexOf(this.connections, result);
this.connections[index] = null;
return result;
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
我怀疑这就是问题所在,在方法的末尾:
this.connectionPool.Putback(sqlConnection);
Run Code Online (Sandbox Code Playgroud)
您只从迭代器中获取while两个元素 - 因此您永远不会完成循环,除非实际上只有一个值从读取器返回。现在您正在使用 LINQ,它将自动调用Dispose()迭代器,因此您的using语句仍将处理读取器 - 但您不会将连接放回池中。如果你在一个finally街区里这样做,我想你会没事的:
var sqlConnection = this.connectionPool.Take();
try
{
// Other stuff here...
using (var reader = this.selectWithSourceVectorCommand.ExecuteReader())
{
while (reader.Read())
{
yield return ReaderToVectorTransition(reader);
}
}
}
finally
{
this.connectionPool.Putback(sqlConnection);
}
Run Code Online (Sandbox Code Playgroud)
或者理想情况下,如果您的连接池是您自己的实现,请Take返回一些实现的内容IDisposable,并在完成后将连接返回到池中。
这是一个简短但完整的程序,用于演示正在发生的情况,不涉及任何实际的数据库:
using System;
using System.Collections.Generic;
using System.Linq;
class DummyReader : IDisposable
{
private readonly int limit;
private int count = -1;
public int Count { get { return count; } }
public DummyReader(int limit)
{
this.limit = limit;
}
public bool Read()
{
count++;
return count < limit;
}
public void Dispose()
{
Console.WriteLine("DummyReader.Dispose()");
}
}
class Test
{
static IEnumerable<int> FindValues(int valuesInReader)
{
Console.WriteLine("Take from the pool");
using (var reader = new DummyReader(valuesInReader))
{
while (reader.Read())
{
yield return reader.Count;
}
}
Console.WriteLine("Put back in the pool");
}
static void Main()
{
var data = FindValues(2).Take(2).ToArray();
Console.WriteLine(string.Join(",", data));
}
}
Run Code Online (Sandbox Code Playgroud)
正如所写的 - 对读者仅找到两个值的情况进行建模 - 输出为:
Take from the pool
DummyReader.Dispose()
0,1
Run Code Online (Sandbox Code Playgroud)
请注意,读者已被处置,但我们从未从池中返回任何内容。如果你改为Main对读者只有一个值的情况进行建模,如下所示:
var data = FindValues(1).Take(2).ToArray();
Run Code Online (Sandbox Code Playgroud)
然后我们一直完成循环while,因此输出发生变化:
Take from the pool
DummyReader.Dispose()
Put back in the pool
0
Run Code Online (Sandbox Code Playgroud)
我建议你复制我的程序并进行试验。确保您了解正在发生的一切...然后您可以将其应用到您自己的代码中。您可能还想阅读我关于迭代器块实现细节的文章。
| 归档时间: |
|
| 查看次数: |
1529 次 |
| 最近记录: |