0

是否有可能通过使用某种逻辑路径的方式来存储集合的所有更改 - 更改发生时的更改 - 以便可以通过基本上“后退”来恢复更改?我假设有些东西需要在变化发生时映射变化,因此恢复它们的过程最终将是线性的。

对任何不一致的地方表示歉意,这不适用于任何特定的语言。相反,这是一个内存问题 - 即可以连续更改的有限大小的集合 *(例如,它可能是用户输入的一些存储)*(例如,在任何给定时间,任何时间 - 没有限制关于可以更改多少)* 以程序方式映射,以便假定新的 - 未来的 - 更改是先前更改的结果 * (在第二个中,镜像存储可用于一直恢复集合的状态到它的初始状态)*。

4

1 回答 1

1

您可能想查看一些函数式数据结构。像 Erlang 这样的函数式语言可以很容易地回滚到早期的状态,因为总是对新的数据结构进行更改,而不是改变现有的数据结构。虽然这个特性可以在内部重复使用,但 Erlang 编程通常在“进程”的顶层大量使用这个特性,以便在任何类型的失败时,它只需抛出一个异常(在非函数式语言中,使用可变数据结构,您可以抛出异常以中止,但恢复原件将是您的程序的工作,而不是运行时的工作)。这是 Erlang 享有盛誉的原因之一。

这种函数式编程风格中的一些有用地应用于非函数式语言,特别是不可变数据结构的使用,例如不可变集合、列表或树。

例如,关于不可变集,人们可能会设计一种面向功能的数据结构,其中修改总是会在给定一些更改和现有集(由添加和删除组成的更改集)的情况下生成一个新集。你会留下旧的集合以供参考(无论是谁);具有自动垃圾收集的语言在不再使用(引用)时回收旧的。

您可以将 id 或 tag 放入您设置的数据结构中,这样您就可以进行一些自省以查看某人拥有的数据结构 id。您还可以捕获生成每个新版本的基础的 id;这给了你一些历史或血统。

如果需要,您还可以在新数据结构中捕获对整个旧数据结构的引用,或者可以在生成所有集合时维护所有集合的全局列表。但是,如果这样做,您将不得不承担更多的存储管理责任,因为如果没有额外的帮助,自动收集器可能不会找到任何未使用(未引用)的垃圾来收集。

数据库设计在他们的事务控制器中做了一些这样的事情。出于您的问题的目的,您可以将数据库视为一个美化的集合。您可能会将 MVCC(多版本并发控制)作为一个在文献中写得相当好的示例。这种技术(临时)保留数据结构的旧快照版本,这意味着突变总是出现在数据的新版本中。维护旧快照,直到没有活动事务引用它;然后被丢弃。当两个并发运行的事务都修改数据库时,它们每个都会根据相同的当前和最新数据集获得一个新版本。(事务控制器确切地知道每个事务基于哪个版本,尽管事务的客户端看不到版本信息。) 假设两个并发事务都选择提交它们的更改,事务控制器中的版本控制识别出第二个提交者正在尝试提交一个不是第一个的逻辑继任者的更改集(因为我们上面假设的两个更改集都是基于在相同的早期版本上)。如果可能,事务控制器将合并更改,就好像第二个提交者真的在处理第一个提交者提交的另一个更新版本一样。(对于何时可以实现这一点有不同的定义,MVCC 说这是没有写冲突的时候,这是一个不太完美的答案,但速度快且可扩展。)但如果不可能,它将中止第二个提交者事务并通知第二个提交者(然后他们有机会,如果他们愿意,从较新的基础开始重试他们的事务)。在幕后,并发事务的各种快照版本可能会共享大部分数据(首先咨询一些特定于事务的更改集),以使快照便宜。通常没有提供 API 来访问旧版本,因此在这个域中,事务控制器知道随着事务退休,他们使用的原始快照版本也可以(引用计数和)退休。

完成的另一个领域是使用 Append-Only-Files。日志记录是记录更改的一种方式;一些数据库 100% 基于面向日志的设计。

BerkeleyDB 有一个很好的日志结构。虽然主要用于恢复,但它确实包含所有历史记录,因此您可以从日志中重新创建数据库(直到您清除日志,在这种情况下您还应该归档数据库)。再次有人必须决定何时可以启动新的日志文件,以及何时可以清除旧的日志文件,您可以这样做以节省空间。

这些数据库技术也可以应用在内存中。(当然,没有什么是免费的;)

无论如何,是的,有些领域可以做到这一点。

  1. 不可变数据结构通过简单地保留旧副本来帮助保存历史;更改总是转到新副本。(效率技术可以让这不像听起来那么糟糕。)
  2. 身份证可以帮助理解血统,而不必保留所有旧副本。
  3. 如果您确实想保留所有旧副本,则必须查看您的域设计以了解何时/如何/是否可以访问旧数据结构,并着眼于如何最终回收它们。如果有的话,您很可能必须帮助参与定义它们的发布方式。或者如何将它们存档以供后代使用,但代价是以后访问速度较慢。
于 2012-10-16T22:32:55.470 回答