可串行化事务隔离级别是否保证非DB代码也被串行化



我需要运行一些DB操作并生成Kafka主题的事件。然而,事件内容取决于从DB中读取的内容以及事件的顺序。考虑这样的操作:

  1. 打开事务
  2. 从表中读取
  3. 更新表
  4. 再次从表中读取
  5. 将事件发送到主题
  6. 提交事务

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

这种情况可能发生吗:

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

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

我认为这个问题可以归结为交易B什么时候会失败?它在第4点失败了吗?因为数据被另一个事务更改了?还是在Kafka事件已经产生之后,它在提交时失败了?

或者有没有可能根本没有失败?类似于此场景:

  1. A读取
  2. A更新
  3. A再次读取
  4. B读取(数据已由A更新(
  5. B更新
  6. B再次读取--没有冲突,因为B在A之后运行所有DB操作
  7. B发射事件
  8. 发射事件

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

最好的解决方案应该是DB angnostic,但如果重要的话,我使用PostgreSQL 10.16

步骤#2"实例B发出事件"将在使用CCD_ 1时失败。

让我们试试吧。让我们创建一个表:

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 $500
update t set amount = amount - 100 where id = 1;
select amount from t where id = 1; -- shows $400
-- wait here while instance #2 works...

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

start transaction isolation level serializable not deferrable;
select amount from t where id = 1; -- shows $500!
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对数据的影响。

最新更新