可序列化事务隔离级别是否保证非数据库代码也被序列化?

Do serializable transaction isolation level guarantee that non-DB code is also serialised?

我需要 运行 一些数据库操作并向 Kafka 主题生成事件。但是,事件内容取决于从数据库中读取的内容和事件顺序。考虑这样的操作:

  1. 开启交易
  2. 阅读自table
  3. 更新table
  4. 再次阅读 table
  5. 向主题发送事件
  6. 提交交易

现在我知道可序列化隔离级别保证数据库中的数据是一致的。同时执行此操作的两个服务实例 运行 总是会读取有效数据,就好像事务是 运行 顺序进行的一样。但是向 Kafka 主题发出事件也是如此吗?

是否可能出现这种情况:

  1. 实例 A 运行s,读取数据并准备要发出的事件
  2. 实例 B 运行s,读取数据就像在 A 之后 运行 并准备要发出的事件
  3. 实例 B 发出事件
  4. 实例 A 发出事件

在上面的场景中,事件顺序是错误的。如果实例 A 在实例 B 引入更改之前根据数据准备事件,则应首先发出其事件。

我觉得这个问题归结为事务B什么时候会失败的问题?它是否在第 4 点失败——因为数据被另一个事务更改了?或者它是否在提交时失败 - 在 Kafka 事件已经产生之后?

或者有没有可能根本就没有失败?就像在这种情况下:

  1. A读
  2. 一个更新
  3. A再读
  4. B 读取(数据已被 A 更新)
  5. B更新
  6. B再读--没有冲突,因为B运行是A
  7. 之后的所有DB操作
  8. B 发出事件
  9. A 发出事件

没有抛出错误,但事件顺序错误

最好的解决方案应该是与数据库无关的,但如果重要的话,我使用 PostgreSQL 10.16

步骤 #2“实例 B 发出事件...”在使用 SERIALIZABLE 时将失败。

我们来试试吧。让我们创建一个 table:

create table t (id int, amount int);

insert into t (id, amount) values (1, 500), (2, 1000);

现在,让我们运行实例#1:

start transaction isolation level serializable not deferrable;

select amount from t where id = 1; -- shows 0

update t set amount = amount - 100 where id = 1;

select amount from t where id = 1; -- shows 0

-- wait here while instance #2 works...

现在,我们 运行 实例 #2:

    start transaction isolation level serializable not deferrable;

    select amount from t where id = 1; -- shows 0!

    update t set amount = amount - 150 where id = 1;
    -- the thread blocks...

现在,回到实例#1:

commit; -- all good

然后回到实例 #2,停止等待:

    Error: ERROR: could not serialize access due to concurrent update
    SQLState:  40001
    ErrorCode: 0

如您所见,当您使用 SERIALIZABLE 时,线程会尝试并行 运行,只要它们不进入彼此的脚趾即可。他们“赢了”并继续处理的那一刻,而另一个则等待;希望另一个不会对数据状态造成太大的损害。在这种情况下,实例 #1 实际上确实对实例 #2 造成了一些损坏(它更改了数据),因此后者失败了。

我想既然你提到了 Kafka,当你说一个实例“发出”某些东西时,你的意思是逻辑解码产生数据。

我认为 SERIALIZABLE 隔离级别不能保证 WAL 的加载顺序或解码顺序。它只涉及 SQL.

对数据的影响