英文原文: http://www.codeproject.com/Articles/110931/Building-High-Performance-Queue-in-Database-for-st
译文:http://www.oschina.net/translate/building-high-performance-queue-in-database-for-storing-orders
引言
几乎在每个地方都能用到队列。在许多web站点里,比如其中的email和SMS都是使用队列来异步发送通知。电子商务网站都是使用队列来存储订单,处理 订单以及实现订单的分发。工厂生产线的自动化系统也是使用队列来按某种顺序运行并发工作任务的。队列是使用很广泛的一种数据结构,它有时可以创建在数据库 里,而不是使用类似于MSMQ那样的特定的队列技术创建。使用数据库技术来运行一个高性能且高可扩展性的队列对我们来说是一个巨大的挑战。当每天进入队列 和从队列中提取的信息达到数百万行的时候,这个队列就很难维护了。我将向你展示在设计类似队列表时常犯的设计错误以及如何使用简单的数据库功能实现队列的 最大性能和强大的可扩展性。
表的读写。在大负载下,入队列和出队列是相互影响而引起锁的竞争、事务死锁、IO超时等等。
当多个接收者试图从同一队列读数据时,它们随机地获取重复项,从而导致重复的处理过程。你需要在队列上实现一些高性能的行锁以至于并发接受器不会接收相同的数据项。
队列表需要以特定的顺序去存储行、以特定的顺序读取行数据,这是一个索引设计的挑战。尽管并不总是先进先出。间或顺序有较高的优先级,无论何时入栈都需要进行处理。
队列表需要以序列化的XML对象或者二进制形式存储, 这带来了存储和索引重建的挑战。由于数据表包含文本和/或二进制,所以你不能在队列表上重建索引。因此队列表每天变的越来越慢,最终查询开始时间超时,最后你不得不关闭服务并重建索引。
出队列的过程中,一批行数据被选中、更新,然后重新处理。你需要一个"State"列定义数据项的状体。出队列时,你只选择特定状态的数
据项。现在State只是一个包含PENDING、PROCESSING、PROCESSED、ARCHIVED等元素的集合。结果是你不能
在"State"列上创建索引,原因是安全性差。队列中可以有成千上万行具有相同的状态。因此
任何由扫面索引的出队列操作都会导致CPU和IO资源紧张以及锁竞争。
在出队列过程中,你不只是从表中移出行,原因是容易引起存储残片。进而,你需要重新订单/任务/通知N次以防止他们在第一次尝试中失败。这意味着行数据需要更长的存储周期、索引持续增长和出队列越来越慢。
你不得不归档以队列表中处理过的数据项到不同的表或者数据库,以保持主队列表的精简。这意味着需要移动大量的具有特殊状态的行到另一个数据库。大量的数据移动产生了的存储碎片,而表的高频度碎片整理降低入栈和出栈的性能。
你需要24X7的工作。你不可能关闭服务而归档大量行数据。这意味者在不影响入栈和出栈,你不得不持续的归档行数据。
如果您已实现这样的队列表,你可能已经遇到了以上挑战中的一个或者更多。本文将给你一个关于如何克服这些挑战的一些技巧,以及如何设计和维护一个高性能的队列表。
SQL Server典型队列
下面以常见的队列类型为例,看看在并发负载时的情况.
1 CREATE TABLE [dbo].[QueueSlow](2 [QueueID] [int] IDENTITY(1,1) NOT NULL,3 [QueueDateTime] [datetime] NOT NULL,4 [Title] [nvarchar](255) NOT NULL,5 [Status] [int] NOT NULL,6 [TextData] [nvarchar](max) NOT NULL
7 ) ON [PRIMARY]
8 GO
9 CREATE UNIQUE CLUSTERED INDEX [PK_QueueSlow] ON [dbo].[QueueSlow]
10 (11 [QueueID] ASC
12 )13 GO
14 CREATE NONCLUSTERED INDEX [IX_QuerySlow] ON [dbo].[QueueSlow]
15 (16 [QueueDateTime] ASC,17 [Status] ASC