Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/75.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 优化此.NET爬虫算法的提示_Sql_.net_Sql Server_Performance_Algorithm - Fatal编程技术网

Sql 优化此.NET爬虫算法的提示

Sql 优化此.NET爬虫算法的提示,sql,.net,sql-server,performance,algorithm,Sql,.net,Sql Server,Performance,Algorithm,我写了一些类似于网络爬虫的东西,它的引擎遵循以下步骤: 阅读Rss链接(参数) 定义Rss项目列表 通过单独的查询检查数据库(SQL SERVER)中是否存在每个链接 如果链接是新的,它将通过单独的查询将字段插入数据库 Public Sub MyTickHandler() Dim NewItems As New List(Of Structures.RSSItem) Dim founded As Boolean = False NewItems = RssReader.

我写了一些类似于网络爬虫的东西,它的引擎遵循以下步骤:

  • 阅读Rss链接(参数)
  • 定义Rss项目列表
  • 通过单独的查询检查数据库(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 server响应超时,但毫无帮助

    对于这个过程,有没有更好的方法或技巧?
    谢谢

    • 仅在for each循环外执行一次提取:
    SELECT id,link FROM posts with(nolock)WHERE link in(@listOfLowerCaseLinks)

    • 将插入的整个操作(每个循环的整个操作)包装到sql事务中。这样,数据库就不必在两者之间提交

    我建议您为此任务将表值参数传递给存储过程。这将允许在单个调用中插入整个列表。下面是一个可以调整实际列长度的示例。在posts表的link列上有一个索引是很重要的。在这个例子中,我假设link是唯一的

    创建表类型和过程的T-SQL:

    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
    

    可能是您的
    查询导致性能下降。运行SQL跟踪以查找。如果不是这样,请在探查器下运行应用程序,找出它在何处花费时间。@500 InternalServerError我以前在单个处理上尝试过探查,所有命令都是正常的,但实际上探查器不允许该进程以正常方式处理150个高处理线程!ThanksI将在insert上测试sql事务。谢谢;)批量获取也非常重要。它将每两分钟提取查询的数量从150减少到1。如果您的
    链接
    列已编制索引,则大部分时间将用于往返sql server,而不是数据本身。加速速度几乎快150倍是可以预期的耶!我没有注意到。。这对我会有很大帮助!但关于事务,我对事务没有足够的经验,而且我知道它会在一段时间内锁定其他线程的访问,在并发中使用它是否正确?检查事务使用示例-如果不使用事务作用域,SQL Server仍然会为每个查询创建一个事务。所以,锁定还是会发生的。通过将查询分组到单个事务中,可以帮助SQL Server对数据库文件执行一次刷新。当然,请尽量缩短事务范围。换句话说,首先进行所有繁重的计算,然后在列表中包含所有数据后,在事务范围内只使用insert命令。此外,SQL Server不喜欢具有大量CUD行为的事务(例如,单个事务中有1000多个插入/更新/删除)。感谢您的解决方案和+1。正如@taoufik所说的批量插入(由SQLBulkCopy提供)是一种单次插入的方式,而不需要像“dbo.usp_InsertRssItems”这样的东西。现在我正在用SQLBulkCopy测试我的代码。所以,如果您知道SQLBulkCopy的一些更好的方法或技巧,我将非常感谢您来描述..表值参数由SqlClient批量复制到tempdb中的临时表中。SqlBulkCopy的性能也很好,但不允许条件插入。在这种情况下,我不需要条件插入,因此我认为SqlBulkCopy for inserts是最佳选择。但是对于大量的Select语句,我不确定@taoufik所描述的方式是否是最好的性能!您知道一种只使用不同参数值扩充select语句的好方法吗?
    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