【问题标题】:PostgreSQL - implementing a reliable queuePostgreSQL - 实现一个可靠的队列
【发布时间】:2016-01-02 00:09:13
【问题描述】:

我正在尝试使用 postgres 数据库实现一个具有多个写入器和多个读取器的可靠队列。当队列读取器扫描表并在读取后提交正在进行的事务时如何避免丢失行。

我们有一个读取器使用“检查点”时间分批选择行,其中每个批次获取上一个批次中最后一个时间戳之后的行,而我们缺少行。 (原因:时间戳值基于插入发生的时间(00.00.00)。在重负载下,如果事务需要更长的时间,它会被插入,比如说 10 秒后(00.00.10),读者会错过这一行(row1) 如果它在那 10 秒内读取并找到其 INSERT 时间比 row1 更晚的时间(00.00.05) 的行。问题的完整描述类似于此博客中所写的。http://blog.thefourthparty.com/stopping-time-in-postgresql/ )

上下文相关的先前问题:Postgres LISTEN/NOTIFY - low latency, realtime?

更新:我已将问题从单个读者更新为多个读者。读者阅读的顺序很重要。

【问题讨论】:

  • 所有行都按顺序处理是否至关重要?那么顺序是如何精确地定义的呢?还是您只是想避免丢失行?然后像这里介绍的解决方案应该可以工作:Postgres Update, limit 1
  • 真的需要你用postgresql做这个吗?这种需求很容易被 redis 满足
  • @e4c5 是的。我们目前正在使用 postgresql,并且在负载较高时遇到了我在问题中描述的问题。
  • @ErwinBrandstetter 我相信它们必须按照时间戳的顺序排列。如果订单搞砸了,我们就会丢失行。此外,您建议的链接看起来涉及锁定的行,我担心这会严重影响吞吐量。

标签: postgresql concurrency queue


【解决方案1】:

考虑到多个阅读器,有必要控制每个阅读器已经收到了哪些记录。

另外,据说顺序也是将记录发送给读者的条件。因此,如果在前一个事务之前提交了一些进一步的事务,我们必须“停止”并在它提交后再次发送记录,以保持发送给读取器的记录的顺序。

也就是说,检查实现:

-- lets create our queue table 
drop table if exists queue_records cascade;
create table if not exists queue_records 
(
  cod serial primary key,
  date_posted timestamp default timeofday()::timestamp,
  message text
);


-- lets create a table to save "checkpoints" per reader_id
drop table if exists queue_reader_checkpoint cascade;
create table if not exists queue_reader_checkpoint 
(
  reader_id text primary key,
  last_checkpoint numeric
);



CREATE OR REPLACE FUNCTION get_queue_records(pREADER_ID text)
RETURNS SETOF queue_records AS
$BODY$
DECLARE
    vLAST_CHECKPOINT    numeric;
    vCHECKPOINT_EXISTS  integer;
    vRECORD         queue_records%rowtype;
BEGIN

    -- let's get the last record sent to the reader 
    SELECT  last_checkpoint
    INTO    vLAST_CHECKPOINT
    FROM    queue_reader_checkpoint
    WHERE   reader_id = pREADER_ID;

    -- if vLAST_CHECKPOINT is null (this is the very first time of reader_id), 
    -- sets it to the last cod from queue. It means that reader will get records from now on.
    if (vLAST_CHECKPOINT is null) then
        -- sets the flag indicating the reader does not have any checkpoint recorded
        vCHECKPOINT_EXISTS = 0;
        -- gets the very last commited record
        SELECT  MAX(cod)
        INTO    vLAST_CHECKPOINT
        FROM    queue_records;
    else
        -- sets the flag indicating the reader already have a checkpoint recorded
        vCHECKPOINT_EXISTS = 1; 
    end if;

    -- now let's get the records from the queue one-by-one 
    FOR vRECORD IN 
            SELECT  *
            FROM    queue_records
            WHERE   COD > vLAST_CHECKPOINT 
            ORDER   BY COD
    LOOP

        -- if next record IS EQUALS to (vLAST_CHECKPOINT+1), the record is in the expected order
        if (vRECORD.COD = (vLAST_CHECKPOINT+1)) then

            -- let's save the last record read
            vLAST_CHECKPOINT = vRECORD.COD;

            -- and return it
            RETURN NEXT vRECORD;

        -- but, if it is not, then is out of order
        else
            -- the reason is some transaction did not commit yet, but there's another further transaction that alread did.
            -- so we must stop sending records to the reader. And probably next time he calls, the transaction will have committed already;
            exit;
        end if;
    END LOOP;


    -- now we have to persist the last record read to be retrieved on next call
    if (vCHECKPOINT_EXISTS = 0) then
        INSERT INTO queue_reader_checkpoint (reader_id, last_checkpoint) values (pREADER_ID, vLAST_CHECKPOINT);
    else        
        UPDATE queue_reader_checkpoint SET last_checkpoint = vLAST_CHECKPOINT where reader_id = pREADER_ID;
    end if; 
end;
$BODY$ LANGUAGE plpgsql VOLATILE;

【讨论】:

  • 我确实需要一些“检查点”。您是否建议我为此使用“鳕鱼”?订单也会完全混乱。
  • 我认为在@Chandra 的用例中,有多个读者都在阅读同一张表,但可能在不同的时间和不同的速度。我不清楚 cod 将如何帮助他的用例。
  • @ShintaSmith:我不这么认为。问题清楚地表明:“......有多个作家和一个读者......”。引入“cod”只是为了帮助正确订购,因为 current_timestamp 从交易开始获取时间。但“鳕鱼”不是解决方案的基础。这是“未读”标志!
  • @Chandra:订购是阅读更多记录的条件吗?还是我们可以只获取所有已提交的记录?
  • @ChristianB.Almeida 很抱歉造成混乱。我试图保持用例简单,但这可能会稍微偏离问题。我确实有多个读者,我相信在那种情况下没有未读标志是行不通的。
猜你喜欢
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2017-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 2017-12-28
相关资源
最近更新 更多