代码之家  ›  专栏  ›  技术社区  ›  aggergren

多进程写入的事务封装

  •  2
  • aggergren  · 技术社区  · 16 年前

    我有一个数据库场景(我使用的是oracle),在这个场景中,几个进程向一个表中进行插入,而一个进程从中进行选择。该表基本上用作中间存储器,多个进程(在下面称为写入程序)向其写入日志事件,单个进程(在下面称为读取器)从中读取事件以进行进一步处理。读取器必须读取插入表中的所有事件。

    目前,这是通过从升序序列中为每个插入的记录分配一个id来完成的。读取器周期性地从表中选择一个条目块,其中id大于先前读取块的最大id。比如说:

    SELECT
      *
    FROM
      TRANSACTION_LOG
    WHERE
      id > (
        SELECT
          last_id
        FROM
          READER_STATUS
       );
    

    这种方法的问题在于,由于编写器并发操作,因此行并不总是按照其分配的id顺序插入,即使这些行是按顺序升序分配的。也就是说,id=100的行有时在id=110的记录之后写入,因为id=110的行的写入过程在id=100的记录的写入过程之后开始,但是首先提交。如果读卡器已经读取id=110的行,则可能导致读卡器丢失id=100的行。

    强制写入程序在表上使用排他锁可以解决问题,因为这会强制它们顺序插入,同时也会让读取器等待任何未完成的提交。然而,这可能不是很快。

    我认为,读者在阅读前等待任何优秀作家的投稿就足够了。也就是说,在所有作者完成之前,作者可以像读者阅读一样长时间地同时操作。

    我的问题是:
    如何指示我的读者进程等待我的作者进程的任何未完成提交?也欢迎对上述问题提出任何其他建议。

    5 回复  |  直到 6 年前
        1
  •  1
  •   AJ.    16 年前

    有趣的问题。听起来你在建立一个很好的解决方案。
    我希望我能帮忙。

    一些建议…

    编写器状态

    您可以创建一个表writer_status,它有一个last_id字段:每个writer在使用要写入日志的id进行写入之前更新此表,但前提是其id大于last_id的当前值。

    读者也检查了这个表,现在知道是否有作者还没有写过。

    读者日志

    这可能更有效率。
    在读卡器进行读取后,它会检查所检索的记录中是否有任何漏洞。
    然后,它将所有丢失的id记录到一个丢失的id表中,并在下次读取时执行如下操作

    SELECT *
    FROM   TRANSACTION_LOG
    WHERE  id > (SELECT last_id
                 FROM   READER_STATUS)
    OR     id IN ( SELECT id from MISSING_IDS ) 
    
        2
  •  1
  •   csgero    16 年前

    您可能希望在读卡器进程中对表设置独占锁。这将等到所有写入程序完成并释放其行锁,以便您可以确保没有未完成的写入程序事务。

        3
  •  1
  •   Bill Karwin    16 年前

    我不会做任何锁定,这会干扰并发性和吞吐量。

    如果您逐行跟踪已处理的日志行,那么也不需要reader_status表。

    我要做的是:在日志表中添加一个新列。例如,将其称为“已处理”。将其设为布尔值,默认值为false(或小整数,默认值为0或其他值)。写入程序在插入时使用默认值。

    当读卡器查询下一个要处理的记录块时,他查询处理为false且id值较低的行。

    SELECT * FROM Transaction_Log
    WHERE processed = 0
    ORDER BY id
    LIMIT 10;
    

    当他处理它们时,读者使用update将处理的结果从false更改为true。因此,下次读者查询一个记录块时,他肯定不会得到已经处理过的行。

    UPDATE Transaction_Log
    SET processed = 1
    WHERE id = ?;  -- do this for each row processed
    

    此更新不应与写入程序执行的插入操作冲突。

    如果有任何行是由其他写入程序按顺序提交的,则读取器在下次查询时将看到这些行,前提是他总是按id列从最低值到最高值的顺序处理这些行。

        4
  •  0
  •   Constantin    16 年前

    既然你知道 last_id 由Reader处理,您可以通过以下方式请求下一个工作项:

    select * from Transaction_log where id = (
      select last_id + 1 /* or whatever increment your sequencer has */
        from Reader_status)
    
        5
  •  0
  •   Community CDub    8 年前

    我同意AJ的解决方案( link )此外,以下建议可能有助于减少孔的数量。

    1)使用 Oracle Sequence 创建id并使用 auto-increment 如下所示

    INSERT INTO transaction_table VALUES(id__seq.nextval, <other columns>);
    

    2)使用 autoCommit(true) 这样插入将立即提交。

    这两个步骤将大大减少孔的数量。仍然存在这样一种可能性:一些插入首先开始,但随后提交,中间发生了读取操作。