0

我有两个脚本;其中一个将行插入到数据库中,而另一个处理新输入的、迄今为止未处理的行。

CREATE TABLE table (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, col1 VARCHAR(32), col2 VARCHAR(32));

所以第一个脚本做了几个单独的插入查询:

INSERT INTO table (id, col1 ,col2) VALUES (0, 'val1_1', 'val1_2');
INSERT INTO table (id, col1 ,col2) VALUES (0, 'val2_1', 'val2_2');
INSERT INTO table (id, col1 ,col2) VALUES (0, 'val3_1', 'val3_2');
...

然后第二个脚本使用类似这样的东西来选择未处理的行:

SELECT * FROM table WHERE id > (SELECT MAX(id FROM table_processed)) ORDER BY id LIMIT 1000;
(do some processing)
(for each id processed from table: INSERT INTO table_processed (id) VALUES ({table.id});)

有时,第一个脚本需要插入 5000 行。我注意到至少有一个实例是处理脚本似乎跳过了许多行(基本上跳过了其中的 3000 行),并且想知道是什么导致了这种情况以及如何防止它(如果它跳过它们一次,那么下次它会继续跳过它们,因为它使用 > MAX(id))。

或者这不应该发生?(在这种情况下,我猜第二个脚本查询一定是错误的)

4

1 回答 1

0

如果 2 个插入事务正在运行,并且稍后的事务(= 获得更高的 auto_incremented id)更早完成,则那些更高的自动增量 id 对其他事务(即:您的处理)更早可见,然后较低的事务(在尚未提交的事务,甚至可能是回滚的事务)。每个 INSERT 都会获得一个全局序列的 id,因此这两个事务甚至不能有一个单一的 id 范围,而是创建一种对所述范围的条带化使用。一个好的工作方法是永远不要依赖 auto_incremented id 的顺序或值,不要将它们用于标识符之外的任何内容。

最明显的解决方案是:

  1. 不要使用那个 MAX(id),而是对 table_processed 进行表的 LEFT JOIN,并使用 table_processed 中尚不存在的那些,但这在选择方面可能很重。
  2. 让 INSERT 对表执行独占 LOCK(在繁忙的情况下不受欢迎,您似乎已经有多个并发 INSERT)。
  3. 让 INSERT 使用processed=0索引列完成(可能这只是默认值,您可以在插入中省略它),然后在完成时SELECT .. FROM table WHERE processed=0设置为1

一个简单的错误就是说:好的,我将在每次插入后 COMMIT 以便尽快完成事务,这仍然容易受到竞争条件的影响,所以不要使用它。

于 2013-03-22T16:29:04.757 回答