DataReader和大型数据集

时间:2014-06-05 18:58:10

标签: sql-server tsql ado.net sqldatareader datareader

我有一个返回10k到20k行的查询。我将此数据转储到IEnumerable<T>,据我所知,这是最快的方式:

using (var rdr = cmd.ExecuteReader()) {
    var trackIdCol = rdr.GetOrdinal("TrackId");
    var dateTimeCol = rdr.GetOrdinal("ActDateTime");
    var clicksCol = rdr.GetOrdinal("Clicks");
    var ipCol = rdr.GetOrdinal("IPAddress");
    while (rdr.Read()) {
        yield return new SiteClick() {
            TrackId = (int)rdr[trackIdCol],
            DateTime = (DateTime)rdr[dateTimeCol],
            Clicks = (int)rdr[clicksCol],
            IPAddress = rdr[ipCol] as string
        };
    }
}

查询大约需要11秒才能返回SSMS和SSDT中的所有结果,但上面的代码需要2分钟。我必须在这里做错事。如果有帮助,SqlDataAdapter.Fill()也需要大约2分钟才能运行。

值得注意的是,我们的数据库可怕地未被优化。事实上,在SSMS中从查询中获取结果需要11秒才是荒谬的,但我必须使用我得到的结果。如果查询在SSMS中快速执行,但空while(rdr.Read()){}仍然需要2分钟,那么数据库可能仍然是问题吗?

1 个答案:

答案 0 :(得分:1)

您无法转储到IEnumerable中,因为它是必须在场景后面实现的接口。在您的样本产量中用作此类实现。但产量相当昂贵。对于每次迭代,其状态都会被保存,然后恢复,对于数据库对象来说这很慢。

数据库阅读器本身是最快的迭代器。所以,如果你可以直接使用它 - 这将 是最快的方式。但是有一个缺点,因为这样的数据库游标可能会锁定 用于从其他线程访问的db表。因此,在单用户环境中使用它或 什么时候可以读取未提交的数据。

为了减少锁定时间,您可以将数据转储到内存中的IEnumerable实现中。 例如,List。只需设置更大的容量以避免频繁分配,所以它 将与底层数组一样快。这种解决方案的缺点是内存使用。但 在你的情况下,这将小于1M。

List<SiteClick> list = new List<SiteClick>(20000);

如果将SiteClick定义为struct而不是class,则可以提高性能和内存使用率。 在这种情况下,列表将包含对象,而不是引用。

如果使用典型的阅读器方法,您可以获得一点额外的性能。

TrackId = rdr.GetInt32(trackIdCol),
DateTime = rdr.GetDateTime(dateTimeCol),
Clicks = rdr.GetInt32(clicksCol),
IPAddress = rdr.GetString(ipCol)

更新:对于SQL Server,当在SSMS中测试查询时,它经常出现这种情况。但是存在一些问题,因为与数据库默认值相比,SMSS具有不同的选项。例如,ARITHABORT。因此,您可以手动设置它以连接到测试。

// Use the same connection as for data reading
var cmdOptions = connection.CreateCommand();
cmdOptions.CommandType = CommandType.Text;
cmdOptions.CommandText = "SET ARITHABORT ON";
cmdOptions.ExecuteNonQuery();

根据MS建议 - http://msdn.microsoft.com/en-us/library/aa933126%28SQL.80%29.aspx,最好确保设置这些选项:

SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
SET CONCAT_NULL_YIELDS_NULL ON
SET NUMERIC_ROUNDABORT ON
SET QUOTED_IDENTIFIER ON
SET NUMERIC_ROUNDABORT OFF