我有一个包含 250,000 条记录的数据库。我正在使用 aDataReader
循环记录并导出到文件。DataReader
仅使用 a和无条件循环记录WHERE
大约需要 22 分钟。我只选择了两列(其中包含大约 1000 个字符的列)id
。nvarchar(max)
22 分钟对于 SQL Server Express 是否正确?1GB 的 RAM 或 1CPU 会对此产生影响吗?
我有一个包含 250,000 条记录的数据库。我正在使用 aDataReader
循环记录并导出到文件。DataReader
仅使用 a和无条件循环记录WHERE
大约需要 22 分钟。我只选择了两列(其中包含大约 1000 个字符的列)id
。nvarchar(max)
22 分钟对于 SQL Server Express 是否正确?1GB 的 RAM 或 1CPU 会对此产生影响吗?
您的运行结果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)
字段)进行排序?在对您来说最好的情况下,您的 PKid
也是一个聚集索引,您要么没有,要么order by
是order by id
:
假设您的varchar(max)
字段名为comments
:
SELECT id, comments
FROM myTable
ORDER BY id
这可以正常工作,但它需要您将所有行读入内存(但它只会对表进行一次解析),因为comments
它是VARCHAR(MAX)
并且不能被索引并且表是 2GB SQL 然后必须将表加载到内存中部分。
可能发生的事情是你有这样的事情:
SELECT id, comments
FROM myTable
ORDER BY comment_date
Wherecomment_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 ? "
22 分钟对于针对 250K 记录的单个基本(非聚合)SELECT 来说听起来太长了(即使 22 秒对我来说听起来也很长)。
要说原因,如果您可以发布一些代码和您的架构定义,这将有所帮助。您是否配置了任何触发器?
每条记录 (2KB) 中有 1K 个字符,250K 记录 (500MB) 应该符合 SQL Express 的 1GB 限制,因此内存不应该只是该查询的问题。
您看到的性能问题的可能原因包括:
更新:我做了一个快速测试。在我的机器上,使用 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();
}
}
}
}
结果(以毫秒为单位的执行时间):
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 重复了测试,结果相似。
从每一行捕获“val”元素将 ExecutionTime 增加到 4093 ms ( string val = (string)reader["val"];
)。使用DataTable.Load(reader)
大约需要 4600 毫秒。
在 SSMS 中运行相同的查询大约需要 8 秒才能捕获所有 256K 行。