Performance SQL Server 2008 R2 Express数据读取器性能

Performance SQL Server 2008 R2 Express数据读取器性能,performance,datareader,sql-server-2008r2-express,Performance,Datareader,Sql Server 2008r2 Express,我有一个包含250000条记录的数据库。我使用DataReader循环记录并导出到文件。只需使用数据读取器循环记录,无需任何条件,大约需要22分钟。我只选择了两列id和一个包含1000个字符的nvarcharmax列 对于SQL Server Express,22分钟听起来正确吗?1GB的RAM或1CPU会对此产生影响吗 22分钟对于一个基本的非聚合选择来说太长了,对于我来说,即使22秒也太长了 为了说明原因,如果您可以发布一些代码和模式定义,这会有所帮助。您是否配置了任何触发器 每个记录2KB

我有一个包含250000条记录的数据库。我使用DataReader循环记录并导出到文件。只需使用数据读取器循环记录,无需任何条件,大约需要22分钟。我只选择了两列id和一个包含1000个字符的nvarcharmax列


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

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();
            }
        }
    }
}
我用sqlenterprise和sqlexpress重复了测试,得到了类似的结果

从每行捕获val元素将执行时间增加到4093毫秒string val=stringreader[val];。使用DataTable.Loadreader大约需要4600毫秒

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

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

rows = 255,000
reserved = 1994320 KB
data = 1911088 KB
index_size = 82752 KB
unused 480KB
这里需要注意的重要一点是reserved=1994320 KB,这意味着您的表大约有1866 MB,在读取由于NVARCHARMAX无法编制索引而未编制索引的字段时,SQL Server必须在限制列之前将整行读取到内存中。因此,您很容易超过1GB内存限制

作为一个简单的测试,删除最后或前150k行,然后重试该查询,看看您得到了什么性能

有几个问题:

您的表在主键上有聚集索引吗?是id字段还是其他字段? 是否对未编入索引的列进行排序,例如“nvarcharmax”字段? 在最适合您的情况下,您的PK是id,也是一个聚集索引,您没有order by或您是order by id:

假设您的varcharmax字段命名为comments:

这可以正常工作,但需要将所有行读取到内存中,但它只能对表执行一次解析,因为注释是VARCHARMAX,无法索引,而表是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 ? "

更多测试-在具有相同数据库的同一台计算机上安装了SQLServer2008R2的完整版本。DataReader在4.3分钟内循环了250000条记录,而使用SQLExpress只需22分钟。您说您只访问了~1k个字符,但实际的表有多大?运行exec sp_spaceused myTable用您的表名替换myTable。NVARCHARMAX的单个记录的最大大小相当大,并且由于NVARCHAR字段上没有索引,您将请求整行,因此如果有另一列,即每行10KB,那么您的250k行实际上是2.5GB等,这意味着它不能全部放入RAM中。rows=255000。保留=1994320 KB,数据=1911088 KB,索引大小=82752 KB,未使用的480KB+1,但请将SqlDataReader也放入using块。如果从datareader循环中的字段读取,会得到什么结果?例如,字符串test=reader[val].ToString;读取val字段会将执行时间增加到4093毫秒
包括捕获所有结果行,大约需要8秒。好吧,对于那些学究来说,我把SqlDataReader放在一个using块中——我是usings的大用户,但是,嘿,这只是一个一次性基准,不是生产代码@econner您的DB与应用程序在同一台机器上吗?查询返回500MB+-如果网络连接速度较慢,则可能导致性能问题。id列为Guid类型,设置为Pk,并针对Pk进行群集。此时,我没有设置WHERE条件或ORDER BY条件,而只是执行SELECT*并循环所有250000条记录。我现在正在运行您提供的3个命令。此外,我将从表中删除5000条记录,看看是否有差异,然后将结果发回
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 ? "