我有一个基本上是序列号发生器的系统。
序列号生成器有两个主要的设计要求(在容错方面):
如果序列号生成器崩溃,则
可以假定序列号是按顺序(1、2、3、4等)发出的。
我第一次尝试解决这个问题是为了让序列号生成器通过附加到单个日志文件来记录每个序列号。这样,如果它崩溃了,它只是从上一个发出的序列号中提取出来,然后沿着它快乐的方向前进。
面临挑战:
那么,以下日志记录方法的优缺点是什么:
重新启动序列号生成器。
在崩溃场景中,序列号生成器无法判断哪个“最近”或“第二个最近”日志文件是损坏的,应该总是安全地从未损坏的序列号+2中重新启动崩溃的生成器。
选项1的实现有点简单,但是选项2使用的磁盘空间更少,而且看起来更智能。
在设计一个可以通过足够的日志文件可靠地从崩溃中恢复的系统方面,我是否遗漏了什么?
发布于 2011-07-31 16:25:54
你需要决定一个“接近”的领域。我的意思是说,如果发生坠机,你愿意损失多少数字。
比方说是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-31 16:33:28
在进行任何类型的设计之前,我认为您确实需要定义和解决这样一个简单的软件可能崩溃的原因。
在我的头上,有几个可能是:磁盘空间不足,没有释放资源的劣质编码,线程问题等等。
如果您的目标只是确保生成的序列号是持久化和唯一的,那么我可能会建议使用类似于sql服务器的方法,并使用NEWSEQUENTIALID()类型的列。这里有一些优点,因为sql server已经解决了问题空间。您可以支持的事务数实际上取决于硬件以及您如何使用它。
这是一种长篇大论的说法:首先,确定你认为它会崩溃的原因。然后看看现有的技术,看看它们是否能满足您的需要,然后再编写类似的东西。
例如。如果线程有问题,请考虑使用web服务器来处理所有这些问题。如果磁盘空间有问题,请考虑升级硬件。如果您在确保持久性方面有问题,请使用SQL (品牌无关紧要)服务器来存储数据。如果生成器机器超载,请考虑不同的体系结构,它允许您智能地负载平衡设备。
另一件事:我认为你所描述的两种方法都不是好的解决方案。如果你真的每秒产生1000秒,那么你可能会考虑负载平衡的生成。这时,您将遇到严重的问题,弄清楚如何使常规日志文件在多个生成点之间保持同步.这一点,sql server已经很擅长。
https://stackoverflow.com/questions/6890516
复制相似问题