2015-02-07 63 views
1

我寫的東西就像一個網絡爬蟲,它的引擎遵循下列步驟操作:技巧優化這個.NET履帶式算法

  1. 閱讀RSS鏈接(參數)
  2. 定義(中)RSS項目列表
  3. 由一個單獨的查詢
  4. 如果鏈接是新的話,會由一個單獨的查詢插入字段DB檢查數據庫(SQL Server)的各環節存在

    Public Sub MyTickHandler() 
        Dim NewItems As New List(Of Structures.RSSItem) 
        Dim founded As Boolean = False 
    
        NewItems = RssReader.ParseRssFile(RssURL) 
    
        Dim connString = Configs.NewsDBConnection 
        Dim myConnection As SqlConnection = New SqlConnection("Server=localhost;Database=db;Integrated Security=SSPI;;Connection Timeout=45;Max Pool Size= 300") 
        myConnection.Open() 
    
        For Each item In NewItems 
         Dim cmdString As String = "SELECT id FROM posts with (nolock) WHERE link LIKE '" & item.link.Trim.ToLower & "'" 
         Dim TheCommand As SqlCommand = New SqlCommand(cmdString, myConnection) 
         Dim result = TheCommand.ExecuteScalar() 
         If result Is Nothing Then 
          TheCommand = New SqlCommand("INSERT INTO posts (link) VALUES ('" & item.link.ToLower.Trim & "')") 
          TheCommand.Connection = myConnection 
          TheCommand.ExecuteNonQuery() 
    
          TheCommand = New SqlCommand("INSERT INTO queue (link,descrip,site,title,category) VALUES ('" & item.link.ToLower.Trim & "','" & StringToBase64(item.description) & "','" & RssSite & "','" & StringToBase64(item.title) & "','" & RssCategory & "')") 
          TheCommand.Connection = myConnection 
          TheCommand.ExecuteNonQuery() 
         End If 
         TheCommand.Dispose() 
        Next 
    
        myConnection.Close() 
        myConnection.Dispose() 
        SqlConnection.ClearPool(myConnection) 
    
    End Sub 
    

這適用於單一呼叫。
但我有一些關於150 Rss鏈接我應該通過線程每隔2分鐘檢查它們中的每一個,所以通過增加SQL查詢的裝載,這個過程以及sql server不會響應和應用程序崩潰! !

我嘗試了一些提示,如增加sql服務器響應超時,但它根本沒有幫助。

這個過程有什麼更好的方法或提示?
感謝

+0

可能是您的'LIKE'查詢導致性能下降。運行SQL跟蹤找出。如果不是這樣,請在探查器下運行該應用程序以查找它花費的時間。 – 2015-02-07 17:33:55

+0

@ 500-InternalServerError我在單個處理之前嘗試了分析,並且所有命令都正常,但實際上分析器不會讓進程以150個高處理線程以正常方式工作!謝謝 – ShayanKM 2015-02-07 17:53:57

回答

1
  • 只做一單取,外面的for-each循環:

SELECT id, link FROM posts with (nolock) WHERE link in (@listOfLowerCaseLinks)

Dim myListOfLinks As New List(Of String) 
... 
TheCommand.Parameters.AddWithValue("@listOfLowerCaseLinks", myListOfLinks) 
  • 裹插入的整體的動作(全換每個循環)到一個sql事務中。這樣,數據庫將不必在中間提交。
+0

我會在插入測試sql事務。謝謝;) – ShayanKM 2015-02-07 17:58:09

+1

批量獲取也非常重要。它將每兩分鐘的讀取查詢次數從150次減少到1次。假設您的'鏈接'列已編入索引,則大部分時間將用於往返於sql服務器,而不是數據本身。加快幾乎150倍的加速是預計 – tofi9 2015-02-07 18:09:17

+0

是啊!我沒有注意到..它會幫助我很多!但關於事務我沒有足夠的事務經驗,我知道它會鎖定其他線程訪問一段時間,在併發中使用它是正確的嗎? – ShayanKM 2015-02-07 18:34:42

1

我建議你傳遞一個表值參數給這個任務的存儲過程。這將允許整個列表被插入到一個調用中。以下是您可以調整實際列長度的示例。在帖子表的鏈接列上有一個索引是非常重要的。我假設鏈接在這個例子中是唯一的。

T-SQL創建表類型和PROC:

CREATE TYPE dbo.linkInfo AS TABLE(
    link varchar(255) NOT NULL PRIMARY KEY 
    ,descrip varchar(255) 
    ,title varchar(255) 
    ); 
GO 

ALTER PROC dbo.usp_InsertRssItems 
    @site varchar(255) 
    ,@category varchar(255) 
    ,@linkInfo dbo.linkInfo READONLY 
AS 

SET NOCOUNT ON; 

DECLARE @InsertedPosts TABLE(link varchar(255)); 

INSERT INTO dbo.posts(link) 
OUTPUT inserted.link INTO @InsertedPosts 
SELECT link 
FROM @linkInfo AS li 
WHERE NOT EXISTS(
    SELECT * 
    FROM dbo.posts AS p 
    WHERE p.link = li.link 
    ); 

INSERT INTO dbo.queue(link,descrip,site,title,category) 
SELECT li.link, li.descrip, @site,li. title, @category 
FROM @linkInfo AS li 
WHERE EXISTS(
    SELECT * 
    FROM @InsertedPosts AS ip 
    WHERE ip.link = li.link 
    ); 
GO 

樣品VB.NET代碼:

Sub MyTickHandler() 

    Dim NewItems As New List(Of Structures.RssItem) 
    Dim founded As Boolean = False 

    NewItems = RssReader.ParseRssFile(RssURL) 

    Dim dt = getNewRssItemDataTable(NewItems) 

    Dim connString = Configs.NewsDBConnection 
    Dim myConnection As SqlConnection = New SqlConnection("Server=localhost;Database=db;Integrated Security=SSPI;;Connection Timeout=45;Max Pool Size= 300") 
    Dim TheCommand As SqlCommand = New SqlCommand("dbo.usp_InsertRssItems", myConnection) 
    TheCommand.Parameters.Add(New SqlParameter("@site", SqlDbType.VarChar, 255)).Value = "z" 
    TheCommand.Parameters.Add(New SqlParameter("@category", SqlDbType.VarChar, 255)).Value = "z" 
    TheCommand.Parameters.Add(New SqlParameter("@linkInfo", SqlDbType.Structured)).Value = dt 
    TheCommand.CommandType = CommandType.StoredProcedure 

    myConnection.Open() 
    TheCommand.ExecuteNonQuery() 

    myConnection.Close() 
    myConnection.Dispose() 

End Sub 

Private Function getNewRssItemDataTable(NewRssItems As List(Of Structures.RssItem)) As DataTable 

    Dim dt As New DataTable 
    dt.Columns.Add("link", GetType(String)).MaxLength = 255 
    dt.Columns.Add("descrip", GetType(String)).MaxLength = 255 
    dt.Columns.Add("title", GetType(String)).MaxLength = 255 

    For Each NewRssItem In NewRssItems 
     Dim row = dt.NewRow 
     dt.Rows.Add(row) 
     row(0) = NewRssItem.link 
     row(1) = NewRssItem.description 
     row(2) = NewRssItem.title 

    Next NewRssItem 

    Return dt 

End Function 

編輯:

我看到你提到你想一個SqlBulkCopy示例。如果插入是無條件的,您可以使用此技術:

Sub executeBulkInsert(connectionString As String, site As String, category As String, NewRssItems As List(Of Structures.RssItem)) 

    Dim dt As New DataTable 

    dt.Columns.Add("link", GetType(String)).MaxLength = 255 
    dt.Columns.Add("descrip", GetType(String)).MaxLength = 255 
    dt.Columns.Add("site", GetType(String)).MaxLength = 255 
    dt.Columns.Add("title", GetType(String)).MaxLength = 255 
    dt.Columns.Add("category", GetType(String)).MaxLength = 255 

    For Each NewRssItem In NewRssItems 
     Dim row = dt.NewRow 
     dt.Rows.Add(row) 
     row(0) = site 
     row(1) = category 
     row(2) = NewRssItem.link 
     row(3) = NewRssItem.description 
     row(4) = NewRssItem.title 

    Next NewRssItem 

    Dim bcp = New SqlBulkCopy(connectionString) 
    bcp.DestinationTableName = "dbo.queue" 

    bcp.WriteToServer(dt) 

End Sub 
+0

感謝您的解決方案和+1。正如@taoufik所說的批量插入(通過SQLBulkCopy)是單一插入的方式,而不需要像「dbo.usp_InsertRssItems」之類的東西。現在我正在用SQLBulkCopy測試我的代碼。所以,如果你知道一些更好的方法或SQLBulkCopy的技巧,我非常感謝你的描述.. – ShayanKM 2015-02-07 20:53:15

+0

通過SqlClient將表值參數批量複製到tempdb中的臨時表中。 SqlBulkCopy對於性能也非常好,但不允許有條件的插入。 – 2015-02-07 21:02:28

+0

在這種情況下,我不需要條件插入,所以我認爲SqlBulkCopy插入是最好的選擇。但是對於填充Select語句我不確定@taoufik描述的方式是否是最好的表現方式!你知道只有不同參數值的擴充選擇語句的好方法嗎? – ShayanKM 2015-02-07 21:13:36