Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using SQL Server as a DB queue with multiple clients

Given a table that is acting as a queue, how can I best configure the table/queries so that multiple clients process from the queue concurrently?

For example, the table below indicates a command that a worker must process. When the worker is done, it will set the processed value to true.

| ID | COMMAND | PROCESSED | |  1 | ...     | true      | |  2 | ...     | false     | |  3 | ...     | false     | 

The clients might obtain one command to work on like so:

select top 1 COMMAND  from EXAMPLE_TABLE  with (UPDLOCK, ROWLOCK)  where PROCESSED=false; 

However, if there are multiple workers, each tries to get the row with ID=2. Only the first will get the pessimistic lock, the rest will wait. Then one of them will get row 3, etc.

What query/configuration would allow each worker client to get a different row each and work on them concurrently?

EDIT:

Several answers suggest variations on using the table itself to record an in-process state. I thought that this would not be possible within a single transaction. (i.e., what's the point of updating the state if no other worker will see it until the txn is committed?) Perhaps the suggestion is:

# start transaction update to 'processing' # end transaction # start transaction process the command update to 'processed' # end transaction 

Is this the way people usually approach this problem? It seems to me that the problem would be better handled by the DB, if possible.

like image 498
Synesso Avatar asked Sep 04 '10 09:09

Synesso


People also ask

Can a SQL Server handle multiple connections?

SQL Server allows a maximum of 32,767 user connections.

How do I create a queue in SQL Server?

ON filegroup | [DEFAULT]Specifies the SQL Server filegroup on which to create this queue. You can use the filegroup parameter to identify a filegroup, or use the DEFAULT identifier to use the default filegroup for the service broker database.

How do you write a SQL query for many-to-many relationships?

When you need to establish a many-to-many relationship between two or more tables, the simplest way is to use a Junction Table. A Junction table in a database, also referred to as a Bridge table or Associative Table, bridges the tables together by referencing the primary keys of each data table.


1 Answers

I recommend you go over Using tables as Queues. Properly implemented queues can handle thousands of concurrent users and service as high as 1/2 Million enqueue/dequeue operations per minute. Until SQL Server 2005 the solution was cumbersome and involved a mixing a SELECT and an UPDATE in a single transaction and give just the right mix of lock hints, as in the article linked by gbn. Luckly since SQL Server 2005 with the advent of the OUTPUT clause, a much more elegant solution is available, and now MSDN recommends using the OUTPUT clause:

You can use OUTPUT in applications that use tables as queues, or to hold intermediate result sets. That is, the application is constantly adding or removing rows from the table

Basically there are 3 parts of the puzzle you need to get right in order for this to work in a highly concurrent manner:

  1. You need to dequeue automically. You have to find the row, skip any locked rows, and mark it as 'dequeued' in a single, atomic operation, and this is where the OUTPUT clause comes into play:
    with CTE as (       SELECT TOP(1) COMMAND, PROCESSED       FROM TABLE WITH (READPAST)       WHERE PROCESSED = 0)     UPDATE CTE       SET PROCESSED = 1       OUTPUT INSERTED.*; 
  1. You must structure your table with the leftmost clustered index key on the PROCESSED column. If the ID was used a primary key, then move it as the second column in the clustered key. The debate whether to keep a non-clustered key on the ID column is open, but I strongly favor not having any secondary non-clustered indexes over queues:
    CREATE CLUSTERED INDEX cdxTable on TABLE(PROCESSED, ID); 
  1. You must not query this table by any other means but by Dequeue. Trying to do Peek operations or trying to use the table both as a Queue and as a store will very likely lead to deadlocks and will slow down throughput dramatically.

The combination of atomic dequeue, READPAST hint at searching elements to dequeue and leftmost key on the clustered index based on the processing bit ensure a very high throughput under a highly concurrent load.

like image 189
Remus Rusanu Avatar answered Sep 25 '22 06:09

Remus Rusanu