2012-01-02 158 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