【问题标题】:Correct isolation level for sequential counter顺序计数器的正确隔离级别
【发布时间】:2016-11-18 13:06:51
【问题描述】:

我正在开发一个计费系统(C# 代码、MySQL Galera Cluster 后端、InnoDB 存储引擎),并且对如何为发票生成真正唯一的序列号存有疑问。

通常在其他系统中,我创建了一个独特的服务来获取发票编号,每当生成发票时,我都会向该服务询问一个编号,并且该服务保证了对持有柜台的桌子的独占访问权,因此不会完全有问题。

但是这个新系统是集群化的以实现高可用性,所以这种方法是不可接受的,因为需要运行多个这样的服务。

所以我在这里应用的逻辑是这样的:

  • 开始交易
  • 创建没有序列号的发票
  • 检索串行计数器
  • 将新计数器写入表
  • 更新发票
  • 提交

如果我没记错的话,如果其他事务在当前事务完成之前更新了计数器,那么提交将抛出异常,然后我可以重试该操作,这将确保发票编号的顺序。

所以我的问题是,哪一个是实现此目的的正确隔离级别? READ_COMMITED 是否足够或可能会产生重复项?或者有更好的方法来解决这个问题?

【问题讨论】:

  • 为什么投反对票?
  • 你的意思是 NDB 集群吗?加莱拉集群?还是其他口味?
  • 这是一个galera集群

标签: c# mysql transactions innodb galera


【解决方案1】:

实际上,如果您不小心,这两种隔离级别都会给您带来麻烦。

除了技术差异(例如,它们锁定了多少行)之外,READ COMMITTEDREPEATABLE READ 在处理以下情况的方式上也有所不同:

start transaction;
select no from counters where type = 'INVOICE';
-- some other session changes the value and commits it
select no from counters where type = 'INVOICE';

READ COMMITTED 会给你两个不同的结果,REPEATABLE READ 会给你两个select 的旧值。但是这两种隔离级别都不会阻止任何人更改值,因此您不希望出现任何一种情况。

重要的是锁定你要更改的行,这样其他人就无法更改它:

start transaction;
select no from counters where type = 'INVOICE' for update;
update counters set no = @newvalue where type = 'INVOICE';

如果计算简单,则先进行实际更新:

start transaction;
update counters set no = no + 1 where type = 'INVOICE';
select no from counters where type = 'INVOICE';

假设您的表看起来像这样(并且您不查询例如select max(no) from invoices 来获取最后一个数字),两个隔离级别都可以工作。它们的主要区别在于它们锁定的(多少行)。如果您在(在我的示例中)type 上有一个索引,它们的行为将完全相同。

然后,该决定将取决于您的其余查询。 repeatable read 通常是一个不错且安全的选择(默认是有原因的);如果您降低它,您可能需要更加努力地考虑潜在的问题,但可能会获得一些性能/更少的阻塞。

您没有指定如何设置集群,显然您必须确保它们都使用同一张表或在您的主服务器上使用不同的偏移量。

对于您的问题,当另一个事务尝试更改值时会发生什么:第二个事务将在需要锁定资源的点等待,直到第一个事务释放它(通常是在它准备好时),您只会得到一个达到超时(或检测到死锁)时出现异常。

【讨论】:

  • 感谢您提供如此详细的答案。我现在更清楚了。关键是更新,我可以在选择之前安全地增加计数器,我喜欢这种方法。
  • @Gusman - 建议您仔细检查这个概念,也许在单独的桌子上。 Galera 中有很多细微差别可能会影响您的严格要求。
  • 序列存储在一个单独的表中仅用于此目的,发票在其他表中,每个企业在序列表上都有自己的记录,我不使用任何类型的总和或动态计算序列的数据,只是一个普通的整数,所以如果表锁真的按预期工作应该没有问题,不是吗?
猜你喜欢
  • 2020-08-29
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 2019-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多