我想了解MySqlDataReader(或一般IDataReader)的怪癖。虽然在互联网上进行研究,但我发现了很多关于如何使用MySqlDataReader的资源,但很少关于幕后发生的事情。我问的是因为我发现在一些基准测试中我执行MySqlCommand.ExecuteReader()
所需的时间比用MySqlDataReader.Reader()
读取所有数据集要小几个数量级。这尤其适用于大型数据集。举一个例子:我正在读取~740000行,查询执行需要80-100毫秒,读取所有数据大约需要15秒。另一个例子是读取~2200行,查询时间约为200毫秒,读取所有数据约为1秒。
根据High Performance MySQL检索的数据缓存在公共连接器中(第3版,第212页),我认为这也适用于Connector/Net。据我所知,在740000行的情况下,可能并非所有数据都可以或应该被缓冲,但是应该可以轻松缓冲第二个示例中的2200行(我请求不超过5-7列)。
在不读取数据库的情况下创建可比较数量的数据结构需要<1 ms(使用System.Diagnostics.Stopwatch测量),因此这不是瓶颈。我想知道如果数据被缓冲,为什么从读者那里读取需要花费很多时间。
答案 0 :(得分:1)
要了解MySqlDataReader
的工作方式,您需要了解MySQL协议。假设未调用MySqlCommand.Prepare()
,则将使用text protocol。
MySqlCommand.ExecuteReader
向服务器发送COM_QUERY
数据包。 MySQL Server回复text resultset。其中包含一个标题,其中包含有关结果集中各列以及所有行的元数据。
实际上,我发现直到查询“完成”(例如,所有WHERE
和ORDER BY
子句都已求值),服务器才返回列元数据。在复杂的查询中,这可能需要花费一些时间。返回列元数据后,MySqlCommand.ExecuteReader
返回一个MySqlDataReader
对象。因此,“执行查询”是您要测量的第一个延迟。
然后,标准while (reader.Read()) { }
循环继续读取从服务器流回的行数据包。此循环的速度取决于服务器发送数据包的速度以及客户端库可以反序列化它们的速度。有些库比另一些库快得多,例如,MySqlConnector可以读取大量行almost twice as fast作为Connector / NET(由于代码效率更高)。但是您观察到的大部分时间只是在接收然后读取行。
从网络堆栈中检索数据之前,在进行反序列化之前存在一定的开销,这可能占总时间的很大一部分。 .NET的新"Pipelines" feature是为了解决这个问题,因此将来我们可能会看到更快的MySQL连接库。