SQL Server中的优先队列

5

我目前正在使用C#构建一个网页爬虫。为了排队那些还没有被爬取的URL,我使用SQL Server。它的速度相当快,但随着时间的推移,它开始变得非常庞大,这会减慢我的存储过程。

CREATE TABLE PriorityQueue
(
ID int IDENTITY(0,1) PRIMARY KEY,
absolute_url varchar (400),
depth int,
priorty int,
domain_host varchar (255),
);

CREATE INDEX queueItem ON PriorityQueue(absolute_url);
CREATE INDEX queueHost ON PriorityQueue(domain_host);

这是我用来管理队列的表格。优先级从1到5,其中1为最高优先级。如下图所示,我还在下面使用索引来存储我的存储过程。 向队列中添加新项的步骤:
DROP PROCEDURE IF EXISTS dbo.Enqueue
GO
CREATE PROCEDURE dbo.Enqueue(@absolute_url varchar(255), @depth int, @priorty int, @host varchar(255))
AS
BEGIN
    INSERT INTO [WebshopCrawler].[dbo].[PriorityQueue] (absolute_url, depth, priorty, domain_host) VALUES (@absolute_url, @depth, @priorty, @host);
END
GO

获取优先级最高的项目的步骤:

DROP PROCEDURE IF EXISTS dbo.Dequeue
GO
CREATE PROCEDURE dbo.Dequeue
AS
BEGIN
    SELECT top 1 absolute_url, depth, priorty
    FROM [WebshopCrawler].[dbo].[PriorityQueue]
    WHERE priorty = (SELECT MIN(priorty) FROM [WebshopCrawler].[dbo].[PriorityQueue])
END
GO

这个程序在处理更大的数据时会变得非常缓慢。
删除已出列项目的步骤:
DROP PROCEDURE IF EXISTS dbo.RemoveFromQueue
GO
CREATE PROCEDURE dbo.RemoveFromQueue(@absolute_url varchar(400))
AS
BEGIN
    DELETE 
    FROM [WebshopCrawler].[dbo].[PriorityQueue]
    WHERE absolute_url = @absolute_url
END
GO

我尝试使用了许多不同的索引,但好像都没有使过程变得更快。我希望有人能提出改进的想法。

1个回答

5
请阅读使用表格作为队列。重要问题如下:
  • 您必须根据出队策略组织表格。使用IDENTITY作为主键完全没有意义。应该基于优先级和出队顺序使用聚簇索引。
  • 您必须以单个语句的形式进行原子出队,使用DELETE ... OUTPUT ...

因此,应该是以下内容:

CREATE TABLE PriorityQueue
(
  priority int not null,
  enqueue_time datetime not null default GETUTCDATE(),
  absolute_url varchar (8000) not null,
  depth int not null,
  domain_host varchar (255) not null,
);

CREATE CLUSTERED INDEX PriorityQueueCdx on PriorityQueue(priority DESC, enqueue_time);

CREATE PROCEDURE dbo.Dequeue
AS
BEGIN
    with cte as (
       SELECT top 1 absolute_url, depth, priority
       FROM [PriorityQueue] with (rowlock, readpast)
       ORDER BY priority DESC, enqueue_time)
     DELETE FROM cte
         OUTPUT DELETED.*;
END
GO

default GETUTCDATE() >> 最好给这个约束命名,而不是让 SQL Server 分配一个随机的名字。我知道这只是举例说明 =) 但是人们可能会盲目地复制它,认为不给约束命名是一个好习惯。 - TT.
其次,如果使用相同的“enqueue_time”添加行,则无法保证排序,这将在快速插入或批量插入时发生。这与队列的概念相违背。 - TT.
你是对的,我已经尝试了上面的方法,它可以正常工作,但由于多线程的原因,无法同时插入URL。 - R.hagens
2
如果在完全相同的时间添加了两个条目,那么哪一个应该先出队?我的观点是,如果它们在完全相同的时间添加,则除了随机性外,没有其他东西来决定哪个应该首先出队。它们在排序上是相等的。 - Peter Henell
Datetime的分辨率为.000、.003和.007。在这些值之间的数值会被四舍五入到最接近的可能值。插入时间为.001和.000的行都会被存储为~.000。你明白我的意思吗?抱歉,这个答案中描述的队列已经损坏了。 - TT.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接