3

我想了解 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 毫秒(使用 System.Diagnostics.Stopwatch 测量),因此这不是瓶颈。我想知道如果数据被缓冲,为什么从阅读器读取需要这么多时间。

4

1 回答 1

2

要了解其MySqlDataReader工作原理,您需要了解 MySQL 协议。假设MySqlCommand.Prepare()未调用,则将使用文本协议

MySqlCommand.ExecuteReader向服务器发送一个COM_QUERY数据包。MySQL 服务器回复一个文本结果集。这包含一个标题,其中包含有关结果集中列的元数据,然后是所有行。

在实践中,我发现在查询“完成”之前,服务器不会返回列元数据(例如,所有WHEREandORDER BY子句都已评估);在复杂的查询中,这可能需要相当长的时间。返回列元数据后,MySqlCommand.ExecuteReader返回一个MySqlDataReader对象。因此,“执行查询”是您测量的第一个延迟。

然后标准while (reader.Read()) { }循环继续读取从服务器流回的行数据包。这个循环的速度取决于服务器发送数据包的速度以及客户端库反序列化它们的速度。一些库比其他库快得多,例如,MySqlConnector 读取大量行的速度几乎是 Connector/NET 的两倍(由于更高效的代码)。但是您观察到的大部分时间只是接收然后读取行。

在反序列化之前从网络堆栈中检索数据时存在一定的开销,这可能占总时间的很大一部分。.NET的新“管道”特性是解决这个问题的一种尝试,因此我们将来可能会看到更快的 MySQL 连接库。

于 2018-08-02T05:16:54.587 回答