Using a database table as a queue

前端 未结 9 2198
既然无缘
既然无缘 2020-12-02 07:45

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

相关标签:
9条回答
  • 2020-12-02 08:41

    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!

    0 讨论(0)
  • 2020-12-02 08:45

    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.

    0 讨论(0)
  • 2020-12-02 08:45

    Create a clustered index over a date (or autoincrement) column. This will keep the rows in the table roughly in index order and allow fast index-based access when you ORDER BY the indexed column. Using TOP X (or LIMIT X, depending on your RDMBS) will then only retrieve the first x items from the index.

    Performance warning: you should always review the execution plans of your queries (on real data) to verify that the optimizer doesn't do unexpected things. Also try to benchmark your queries (again on real data) to be able to make informed decisions.

    0 讨论(0)
提交回复
热议问题