我需要运行一些DB操作并生成Kafka主题的事件。然而,事件内容取决于从DB中读取的内容以及事件的顺序。考虑这样的操作:
- 打开事务
- 从表中读取
- 更新表
- 再次从表中读取
- 将事件发送到主题
- 提交事务
现在我知道了可序列化隔离级别可以保证数据库中的数据是一致的。同时运行此操作的两个服务实例总是读取有效数据,就好像事务是按顺序运行一样。但对于向卡夫卡主题发出事件也是真的吗?
这种情况可能发生吗:
- 实例A运行、读取数据并准备要发出的事件
- 实例B运行,读取数据,就像它在A之后运行一样,并准备要发出的事件
- 实例B发出事件
- 实例A发出事件
在上述场景中,事件顺序是错误的。如果实例A在实例B引入更改之前根据数据准备了事件,则应首先发出其"事件。
我认为这个问题可以归结为交易B什么时候会失败?它在第4点失败了吗?因为数据被另一个事务更改了?还是在Kafka事件已经产生之后,它在提交时失败了?
或者有没有可能根本没有失败?类似于此场景:
- A读取
- A更新
- A再次读取
- B读取(数据已由A更新(
- B更新
- B再次读取--没有冲突,因为B在A之后运行所有DB操作
- B发射事件
- 发射事件
没有抛出错误,但事件顺序是错误的
最好的解决方案应该是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对数据的影响。