SQL Server 中的优先级队列

Posted

技术标签:

【中文标题】SQL Server 中的优先级队列【英文标题】:Priority queue in SQL Server 【发布时间】:2016-11-29 11:13:45 【问题描述】:

我目前正在使用 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】:

请阅读Using tables as Queues。重要问题:

您必须根据出队策略组织表。 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 为其分配一个随机名称。我知道这只是插图 =) 但人们可能会盲目地复制它,认为不命名约束是一种好习惯。 其次,如果添加的行使用相同的enqueue_time,则无法保证排序,这会发生在快速插入或批量插入时。这与队列的想法背道而驰。 TT。你是对的,我已经尝试了上面的方法,它工作正常,但是由于多线程,可以同时插入 url。 如果同时添加两个条目,应该先出列哪个条目?我的观点是,如果它们是同时添加的,那么除了随机性来决定哪个首先要出队之外,别无他法。它们的顺序相同。 日期时间的分辨率为 0.000、0.003 和 0.007。中间的值被四舍五入到这些值中最接近的值。在 ~.001 和 ~.000 处插入的行都存储为 ~.000。看到我要说的了吗?抱歉,因此答案中描述的队列已损坏

以上是关于SQL Server 中的优先级队列的主要内容,如果未能解决你的问题,请参考以下文章

数据结构——优先队列

优先级队列中的不同元素

优先级队列(Priority Queue)

如何避免优先级队列中的饥饿

优先队列和单调队列一样吗?

与java中的优先级队列混淆