2012-01-02 159 views
0

我有一个包含250,000条记录的数据库。我正在使用DataReader来循环记录并导出到文件。只用DataReaderWHERE条件循环记录大约需要22分钟。我只选择了两列(idnvarchar(max)列,其中包含约1000个字符)。SQL Server 2008 R2 Express DataReader性能

22分钟听起来对SQL Server Express是否正确? 1GB的RAM或1CPU会对此产生影响吗?

+0

更多测试 - 在具有相同数据库的相同机器上安装完整版本的SQLServer 2008 R2。 DataReader在4.3分钟内循环了250,000条记录,而SQLExpress则是22分钟。 – econner 2012-01-02 07:04:23

+0

你说你只能访问〜1k个字符,但实际表格有多大?运行'exec sp_spaceused myTable'(用你的表名替换'myTable')。单个NVARCHAR(MAX)记录的最大大小非常大,并且由于您不会在NVARCHAR字段的索引中存在索引,所以您将请求整行,因此如果存在另一列也就是说每行10KB,你的250k行实际上是2.5GB等等,这意味着它不能全部适合RAM。 – Seph 2012-01-04 10:39:10

+0

rows = 255,000。保留= 1994320 KB,数据= 1911088 KB,index_size = 82752 KB,未使用480KB – econner 2012-01-04 17:37:15

回答

0

对于单个基本(非聚合)SELECT对250K记录(甚至22秒听起来对我而言非常长),22分钟听起来太长了。

要说明原因,如果您可以发布一些代码和架构定义,这将有所帮助。你有没有配置任何触发器?

对于每条记录(2KB)中的1K个字符,250K条记录(500MB)应该符合SQL Express的1GB限制,因此内存不应单独作为该查询的问题。

您遇到的性能问题的可能原因包括:

  • 从其他应用程序
  • 具有行这不仅仅是你提到
  • 过多的磁盘碎片的两列宽得多争的表格或DB MDF文件
  • 您的应用和数据库之间的网络连接速度较慢

更新:我做了一个快速测试。在我的机器上,使用SqlDataReader读取250K 2KB行不到1秒。

首先,创建256K行测试表(这个只用了约30秒):

CREATE TABLE dbo.data (num int PRIMARY KEY, val nvarchar(max)) 
GO 
DECLARE @txt nvarchar(max) 
SET @txt = N'put 1000 characters here....' 
INSERT dbo.data VALUES (1, @txt); 
GO 
INSERT dbo.data 
    SELECT num + (SELECT COUNT(*) FROM dbo.data), val FROM dbo.data 
GO 18 

测试网页读取数据并显示统计:

using System; 
using System.Collections; 
using System.Data.SqlClient; 
using System.Text; 

public partial class pages_default 
{ 
    protected override void OnLoad(EventArgs e) 
    { 
     base.OnLoad(e); 
     using (SqlConnection conn = new SqlConnection(DAL.ConnectionString)) 
     { 
      using (SqlCommand cmd = new SqlCommand("SELECT num, val FROM dbo.data", conn)) 
      { 
       conn.Open(); 
       conn.StatisticsEnabled = true; 
       using (SqlDataReader reader = cmd.ExecuteReader()) 
       { 
        while (reader.Read()) 
        { 
        } 
       } 
       StringBuilder result = new StringBuilder(); 
       IDictionary stats = conn.RetrieveStatistics(); 
       foreach (string key in stats.Keys) 
       { 
        result.Append(key); 
        result.Append(" = "); 
        result.Append(stats[key]); 
        result.Append("<br/>"); 
       } 
       this.info.Text = result.ToString(); 
      } 
     } 
    } 
} 

结果(ExecutionTime在毫秒):

IduRows = 0 
Prepares = 0 
PreparedExecs = 0 
ConnectionTime = 930 
SelectCount = 1 
Transactions = 0 
BytesSent = 88 
NetworkServerTime = 0 
SumResultSets = 1 
BuffersReceived = 66324 
BytesReceived = 530586745 
UnpreparedExecs = 1 
ServerRoundtrips = 1 
IduCount = 0 
BuffersSent = 1 
ExecutionTime = 893 
SelectRows = 262144 
CursorOpens = 0 

我用SQL Enterprise和SQL Express重复了测试,机智h类似的结果。

从每行中捕获“val”元素将ExecutionTime增加到4093 ms(string val = (string)reader["val"];)。使用DataTable.Load(reader)花了大约4600毫秒。

在SSMS中运行相同的查询需要大约8秒钟来捕获所有256K行。

+2

+1,但请将'SqlDataReader'放入'using'块中。 – 2012-01-02 08:02:32

+0

如果您从数据读取器循环的某个字段中读取,会得到什么结果?例如,string test = reader [“val”]。ToString(); – econner 2012-01-03 01:31:13

+0

读取“val”字段会将执行时间增加到4093 ms。从SSMS运行查询(包括捕获所有结果行)大约需要8秒。 – RickNZ 2012-01-03 02:49:03

0

你运行exec sp_spaceused myTable结果提供了一个潜在的暗示:

rows = 255,000 
reserved = 1994320 KB 
data = 1911088 KB 
index_size = 82752 KB 
unused 480KB 

这里要注意的重要一点是reserved = 1994320 KB这意味着你的表是一些1866年MB,读书未索引(因为NVARCHAR(MAX)字段时,不能索引)SQL Server必须在限制列之前将整行读入内存。因此,您可以轻松运行超过1GB RAM的限制。

作为一个简单的测试,删除最后(或第一个)150k行并再次尝试查询,看看您获得的性能。

几个问题:

  • 贵台对主键聚集索引(难道是id场或别的东西)?
  • 您是否在未编制索引的列上排序,如`nvarchar(max)字段?

在最适合你的情况你的PK是id,也是一个聚集索引,你要么没有order by或者你是order by id

假设你varchar(max)字段命名为comments

SELECT id, comments 
FROM myTable 
ORDER BY id 

这将工作正常,但它会要求你读取所有行到内存(但它只会做一个解析表),因为commentsVARCHAR(MAX),不能索引和表是2GB SQL然后将不得不将表加载到内存中的部分。

很有可能发生的事情是,你有这样的事情:

SELECT id, comments 
FROM myTable 
ORDER BY comment_date 

comment_date是一个额外的字段不被索引。在这种情况下,行为可能是SQL无法实际对内存中的所有行进行排序,并且最终不得不多次将表进行内存分页,导致出现问题。

在这种情况下,一个简单的解决方案是添加一个索引comment_date。

但是,假设你只有读访问数据库是不可能的,那么另一种解决方案是使数据的你想要一个本地临时表,可以使用下列内容:

DECLARE @T TABLE 
(
id BIGINT, 
comments NVARCHAR(MAX), 
comment_date date 
) 

INSERT INTO @T SELECT id, comments, comment_date FROM myTable 

SELECT id, comments 
FROM @T 
ORDER BY comment_date 

如果这不帮助,然后需要额外的信息,您可以发布您的实际查询以及您的整个表格定义和索引是什么。

超越这一切的运行下面的还原备份重建索引和统计信息后,你可能只是从损坏的统计痛苦(这发生在你备份一个碎片化的数据库,然后将其恢复到一个新的实例):

EXEC [sp_MSforeachtable] @command1="RAISERROR('UPDATE STATISTICS(''?'') ...',10,1) WITH NOWAIT UPDATE STATISTICS ? " 

EXEC [sp_MSforeachtable] @command1="RAISERROR('DBCC DBREINDEX(''?'') ...',10,1) WITH NOWAIT DBCC DBREINDEX('?')" 

EXEC [sp_MSforeachtable] @command1="RAISERROR('UPDATE STATISTICS(''?'') ...',10,1) WITH NOWAIT UPDATE STATISTICS ? " 
+0

id列是类型Guid并设置为Pk并为Pk群集。此时我不设置WHERE条件或ORDER BY条件,而只是执行SELECT *并循环所有250,000条记录。我现在正在运行你提供的3个命令。此外,我还会从表中删除5000条记录,看看它是否有所作为,然后发回结果 – econner 2012-01-06 05:35:07