Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a database table as a queue

I want to use a database table as a queue. I want to insert in it and take elements from it in the inserted order (FIFO). My main consideration is performance because I have thousands of these transactions each second. So I want to use a SQL query that gives me the first element without searching the whole table. I do not remove a row when I read it. Does SELECT TOP 1 ..... help here? Should I use any special indexes?

like image 277
Shayan Avatar asked Feb 01 '10 15:02

Shayan


People also ask

What is a database queue?

A queuing system is composed of producers and consumers. A producer enqueues messages (writes messages to a database) and a consumer dequeues messages (reads messages from the database).

What is SQL queue?

A queue can be the target of a SELECT statement. However, the contents of a queue can only be modified using statements that operate on Service Broker conversations, such as SEND, RECEIVE, and END CONVERSATION. A queue cannot be the target of an INSERT, UPDATE, DELETE, or TRUNCATE statement.

What is queue table in Oracle?

Queues are stored in queue tables. Each queue table is a database table and contains one or more queues. Each queue table contains a default exception queue. Figure 7-1, "Basic Queues" shows the relationship between messages, queues, and queue tables.

What is queue in MySQL?

Q4M (Queue for MySQL) is a message queue licensed under GPL that works as a pluggable storage engine of MySQL, designed to be robust, fast, flexible. It is already in production quality, and is used by several web services (see Users of Q4M).


2 Answers

I'd use an IDENTITY field as the primary key to provide the uniquely incrementing ID for each queued item, and stick a clustered index on it. This would represent the order in which the items were queued.

To keep the items in the queue table while you process them, you'd need a "status" field to indicate the current status of a particular item (e.g. 0=waiting, 1=being processed, 2=processed). This is needed to prevent an item be processed twice.

When processing items in the queue, you'd need to find the next item in the table NOT currently being processed. This would need to be in such a way so as to prevent multiple processes picking up the same item to process at the same time as demonstrated below. Note the table hints UPDLOCK and READPAST which you should be aware of when implementing queues.

e.g. within a sproc, something like this:

DECLARE @NextID INTEGER  BEGIN TRANSACTION  -- Find the next queued item that is waiting to be processed SELECT TOP 1 @NextID = ID FROM MyQueueTable WITH (UPDLOCK, READPAST) WHERE StateField = 0 ORDER BY ID ASC  -- if we've found one, mark it as being processed IF @NextId IS NOT NULL     UPDATE MyQueueTable SET Status = 1 WHERE ID = @NextId  COMMIT TRANSACTION  -- If we've got an item from the queue, return to whatever is going to process it IF @NextId IS NOT NULL     SELECT * FROM MyQueueTable WHERE ID = @NextID 

If processing an item fails, do you want to be able to try it again later? If so, you'll need to either reset the status back to 0 or something. That will require more thought.

Alternatively, don't use a database table as a queue, but something like MSMQ - just thought I'd throw that in the mix!

like image 90
AdaTheDev Avatar answered Oct 20 '22 04:10

AdaTheDev


If you do not remove your processed rows, then you are going to need some sort of flag that indicates that a row has already been processed.

Put an index on that flag, and on the column you are going to order by.

Partition your table over that flag, so the dequeued transactions are not clogging up your queries.

If you would really get 1.000 messages every second, that would result in 86.400.000 rows a day. You might want to think of some way to clean up old rows.

like image 32
Peter Lang Avatar answered Oct 20 '22 04:10

Peter Lang