1

我有一个基本上是序列号生成器的系统。

序列号生成器有两个主要的设计要求(在容错方面):

  1. 签发的所有序列号必须是唯一的(不得重复)
  2. 如果序列号生成器崩溃,它需要一种从中断处重新启动的方法如果需要,可以在从崩溃中恢复时跳过几个序列号,以确保满足要求 #1)

可以假设序列号是按顺序发出的(1、2、3、4等)

我第一次尝试解决这个问题是让序列号生成器通过附加到单个日志文件来记录每个有问题的序列号。这样,如果它崩溃了,它只是从发布的最后一个序列号中获取,然后继续它的快乐方式。

挑战如下:

那么,以下日志记录方法的优缺点是什么:

  1. 有一个附加的日志文件,并以特定大小为上限
  2. 有两个日志文件,都没有附加。第一个日志文件始终包含最近发布的序列号,第二个文件包含第二个最近发布的序列号。日志文件以跷跷板方式写入(即所有偶数序列号最终都放入日志文件“A”,奇数序列号放入日志文件“B”)。最终结果是你总是得到最后两个序列号(并且没有更多),磁盘空间的使用很小,如果序列号生成器在记录最近的序列号时发生崩溃,那么'最近的文件可能已损坏。由于我们在另一个日志文件中也有第二个最近的序列号,我们可以检测到崩溃,并从这个“第二个最近的”序列号重新启动序列号生成器,+2

在序列号生成器无法判断“最新”或“第二个最新”日志文件中的哪个是损坏的崩溃场景中,始终从未损坏的序列号重新启动崩溃的生成器应该是安全的 + 2.

选项 1 实现起来稍微简单一些,但选项 2 使用的磁盘空间更少,而且看起来更智能。

在设计一个可以通过足够的日志文件可靠地从崩溃中恢复的系统方面,我是否遗漏了什么?

4

2 回答 2

1

你需要决定一个“关闭”的领域。我的意思是在崩溃的情况下你愿意失去多少数字。

假设它是1000。

然后你将最大的序列保存在一个文件中。

当需要更新时,您将新编号写入新文件,然后将其重命名为旧文件。这是现代文件系统上的原子操作,它要么工作要么不工作,所以它就像数据库中的提交。它保证您有空间容纳新的序列信息,并且如果确实发生了不愉快的事情,它应该会在不损害当前序列信息的情况下失败。

如果出现故障,您必须停止并中止序列生成器。

这里的关键是文件系统上的数字大于任何已发布的数字。因此,您必须保证它永远不会低于当前发布的数字,否则它将在重新启动时重用数字。

所以,这是程序。

function int getNextSequence() {
    currentSeq = currentSeq + 1; 
    if (currentSeq >= maxSeq) {
        maxSeq = maxSeq + 1000;
        write(maxSeq, "newSeq");
        rename("newSeq", "curSeq");
    }
    return currentSeq;
}

function restartSequence() {
    maxSeq = read("curSeq");

    currentSeq = maxSeq - 1; // This will immediately create a disk update on first use.
 }

这里可能存在一次性错误,未经测试。

附加物:

如果你那么担心,你可以在内存中保留四段数据,并写出两份。或者更好,六和三。

您保存在内存中的数据是计数器的三个副本,以及这些计数器的三个校验和(可能是值的 MD5)。

然后在编写它们时,使用与上述相同的技术,编写,然后重命名。

但是你写值和散列。

你这样做的原因很简单。

如果序列的值与它们的哈希/校验和不匹配,则您知道该对是错误的。

您拥有三个副本,前提是虽然可能有一个损坏,而且不仅在磁盘中,而且在内存中 - 不要忽视潜在的内存错误(如果您想偏执,请按照我说的方式进行),但腐败影响不止一个人的事实在天文数字上是不可能的。

当您确实检测到失败的配对时,您有三个样本可供选择,每个样本都是一个“投票”。选择匹配的两个作为官方值,恢复该值,然后继续。

于 2011-07-31T16:25:54.327 回答
1

在进行任何类型的设计之前,我认为您确实需要定义和解决如此简单的软件可能崩溃 的原因。

我想到的可能是:磁盘空间不足、使用未释放资源的劣质编码、线程问题等。

如果您的目标是简单地确保生成的序列号是持久的且唯一的,那么我可能会建议将 sql server 之类的东西与 NEWSEQUENTIALID() 类型的列结合使用。由于sql server已经解决的问题空间,这里有一定的优势。您可以支持的每秒事务数实际上取决于硬件以及您如何使用它。

这是一种冗长的说法:首先确定你认为它会崩溃的原因。然后看看现有的技术,看看它们是否满足你的需要,然后再继续写这样的东西。

例如。如果您遇到线程问题,请考虑使用 Web 服务器来为您处理所有这些问题。如果您遇到磁盘空间问题,请考虑升级您的硬件。如果您在确保持久性方面遇到问题,请使用 SQL(品牌无关紧要)服务器来存储数据。如果发电机机器过载,请考虑使用不同的架构,让您可以智能地对设备进行负载平衡。


另一件事:我认为您概述的两种方法都不是好的解决方案。如果您确实每秒生成 1000 个,那么您可能会考虑对生成进行负载平衡。在这一点上,您将在弄清楚如何在多个生成点之间保持常规日志文件同步时遇到严重的问题......其中,sql server 已经很擅长了。

于 2011-07-31T16:33:28.970 回答