0

假设我们有一个带有自增主键的表。我想加载所有大于我看到的最后一个 ID 的 ID。

SELECT id
FROM mytable
WHERE id > 10;

使用幼稚的方法,我冒着跳过 ID 的风险:

  • 事务 1 声明 ID 11。
  • 事务 2 声明 ID 12。
  • 事务 2 提交。
  • 我阅读了所有> 10的ID。我看到了 12,下次我会读取所有 >12 的 ID。我跳过了11。
  • 事务 1 提交。出于所有意图和目的,ID 11 现在存在。

作为一种解决方案,我建议进行仔细检查以确保没有中间 ID 即将提交:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

SELECT COUNT(*)
FROM mytable
WHERE id > 10
AND id <= 12; -- Where 12 was the max seen in the first query

如果计数大于在第一个查询中看到的 ID 数,那么肯定有可能在该序列的间隙中提交额外的 ID。

问题是:反过来是否成立?如果计数等于(或小于)在第一个查询中看到的 ID 数,是否保证两者之间不会有任何值?还是我错过了一些可能的情况,即 ID 被认领,但READ UNCOMMITTED查询还没有看到它们?

对于这个问题,请忽略:

  • 手动插入 ID。
  • 倒带自动增量计数器。
4

2 回答 2

1

mysql 在 auto_increment 期间锁定表。

https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html

因此,如果版本中没有错误,通常不会发生该问题。

锁的工作方式类似于信号量/关键部分。

于 2019-11-29T14:18:06.607 回答
0

自 1992 年以来,我一直在使用 SQL 数据库进行开发,但我从未见过使用 READ UNCOMMITTED 是解决任何问题的正确方法的情况。

我猜你正在使用 MySQL 作为一种队列。也就是说,您依赖自动增量 ID 作为队列的头部。

我认为您不能按照您描述的方式执行此操作,因为事务生成其 auto-inc ID 的顺序与它们提交的顺序不同。

我建议您需要另一个名为processed或类似的列。然后您可以查询您尚未处理的记录:

SELECT id FROM mytable WHERE processed = false ORDER BY id

这样,查询将始终返回您尚未看到的任何记录。如果在您已经看到 ID 12 之后提交了 ID 11,它将在您下次运行此查询时显示。

一旦你完成了你要对记录做的任何事情,然后:

UPDATE mytable SET processed = true WHERE id = ?

一个更好的解决方案,不需要processed列,是使用消息队列来补充 SQL 数据库。

当客户端添加记录时,他们还应该将刚刚插入的记录的 ID 发布到消息队列中。重要的是,此客户端在提交记录发布到消息队列,否则消息队列的使用者可能会收到他们看不到的 ID 的通知。

于 2019-11-29T15:36:23.197 回答