如何在两个数据库(文件系统和关系型数据库)之间保证原子性?

10
我正在进行一个在线文件管理项目。我们在数据库(SQL Server)上存储引用,而将文件数据存储在文件系统中。我们在上传文件时以及删除文件时遇到了文件系统和数据库之间协调的问题。首先,我们需要在数据库中创建引用或在文件系统上存储文件。问题是,如果我们首先在数据库中创建引用,然后再在文件系统上存储文件,在存储文件时发生任何类型的错误,则会在数据库中创建该文件的引用,但没有文件数据存储在文件系统中。请给我一些解决这种情况的方案。我非常需要它;原因是什么?

重复:https://dev59.com/CEzSa4cB1Zd3GeqPp9IP - NealB
3个回答

4
这其实比你想象的要简单一些。
首先,你需要决定“唯一真相来源”。
也就是说,在任何给定的时间点上,文件系统或数据库是正确的,哪一个是它?
这样做的原因是因为这样更容易解决冲突。
你应该假设数据库是你的源,文件系统是数据库的阴影。这似乎是违反直觉的,因为如果一个条目不在文件系统中,它怎么可能存在于数据库中呢?显然不可能。但是,基本上,如果文件不在数据库中,那么“它根本不存在”。因此,文件系统反映了数据库的状态,而不是反过来。
在这些假设的基础上,你最终得出以下冲突解决规则。
对于任何给定的文件:
File Exists    DB Entry Exists   Action
   Yes            Yes            No action, normal state
   No             Yes            Error -- missing file, "should never happen"
   No             No             No action, normal state
   Yes            No             Delete the file, but no error.

在上传文件时,存在一个灰色地带——即上传了文件但尚未被数据库确认。

解决这个问题的方法是需要将文件上传到临时目录中,以便进行分阶段上传。

实现这个方法的简单方式是将文件上传到不同的目录,但在相同的物理文件系统上,或者使用临时文件名将其上传到最终位置。无论哪种方式,文件都可以通过其名称或位置轻松地识别为“正在处理”。

你希望在同一文件系统上对这个文件进行“分阶段”处理有两个原因。一是磁盘空间。如果在上传时磁盘没有填满,那么你就知道它将适合于最终的存放位置(它已经“预留”了空间)。二是当你最终放置文件时,该操作必须是原子性的。在现代文件系统上,同一文件系统上的文件重命名操作是原子性的。基本上,你不能让文件“半路重命名”,即使它必然“覆盖”了一个现有文件(原始文件也会在重命名操作期间被删除)。

一旦进行了分阶段处理,你的操作就变成了:

Start DB transaction
Rename file
Add DB record
Commit transaction

如果重命名文件操作失败,您需要中止并回滚数据库事务,因此也会撤销该条目。如果重命名成功,但是数据库失败了?那么您就会处于上面列出的状态#4。请重试上传,直到成功为止。
要删除文件,请执行以下操作:
Start DB Transaction
Delete DB record
Commit transaction
Delete file from file system

如果数据库删除失败,不要删除文件。如果数据库删除成功,但文件删除失败,则回到状态#4。
最后,您有一个收割者进程,定期(每天、每周等)将数据库与文件系统进行比较,删除不在数据库中的任何文件。由于数据库是“唯一的真相来源”,因此这两个存储库最终将保持同步。
如果缺少具有数据库记录的文件,则会出现“数据损坏”。不要这样做。这是一个错误,或者有人在穿越您的文件系统。
上传过程的重试特性和删除过程的快速失败为您提供了一种伪两阶段提交过程,可以轻松检查什么是对的和错的,并且易于纠正到正确的状态。

你有关于“在现代文件系统上,同一文件系统上的文件重命名操作是原子性的”这方面的参考资料吗? - v1d3rm3
1
@v1d3rm3,快速谷歌搜索发现了这个链接:https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html。请注意它特别提到了“rename”,但是在系统中,链接和重命名都是相关的。 - Will Hartung

1

为了使两阶段提交正常工作,您需要一个支持数据库和文件系统事务的事务协调器。

由于您没有指定数据库、编程语言或平台,因此这是我能提供的答案。


0
在Windows Vista、Windows Server 2008或以后的Windows操作系统中,您可以使用事务来管理对NTFS的访问权限
通过使用此功能,如果您在.NET中编写程序,可以使用System.Transactions命名空间将对文件系统和数据库的更新作为一个原子单元执行。
我不知道其他操作系统上是否有事务性文件系统。这并不意味着它们不存在。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接