查看源代码 Mnesia 系统信息

以下主题包括:

  • 数据库配置数据
  • 核心转储
  • 转储表
  • 检查点
  • 启动文件、日志文件和数据文件
  • 启动时加载表
  • 从通信故障中恢复
  • 事务的恢复
  • 备份、恢复、回退和灾难恢复

数据库配置数据

可以使用以下两个函数检索系统信息。有关详细信息,请参阅参考手册。

核心转储

如果 Mnesia 出现故障,系统信息将转储到文件 MnesiaCore.Node.When。此文件中包含的系统信息类型也可以通过函数 mnesia_lib:coredump() 生成。如果 Mnesia 系统行为异常,建议在错误报告中包含 Mnesia 核心转储文件。

转储表

类型为 ram_copies 的表按定义仅存储在内存中。但是,这些表可以转储到磁盘,可以定期转储,也可以在系统关闭之前转储。函数 mnesia:dump_tables(TabList) 将一组 RAM 表的所有副本转储到磁盘。可以在转储到磁盘的同时访问这些表。要将表转储到磁盘,所有副本的存储类型必须为 ram_copies

表内容放置在磁盘上的 .DCD 文件中。当 Mnesia 系统启动时,RAM 表最初会从其 .DCD 文件加载数据。

检查点

检查点是一个跨越一个或多个表的事务一致状态。激活检查点后,系统会记住该组表的当前内容。检查点保留表的事务一致状态,允许在检查点处于活动状态时读取和更新表。检查点通常用于将表备份到外部介质,但在 Mnesia 中也用于其他目的。每个检查点都是独立的,一个表可以同时参与多个检查点。

每个表都在检查点保留器中保留其旧内容。对于性能关键型应用程序,认识到与检查点相关的处理开销可能很重要。在最坏的情况下,检查点保留器会消耗比表本身更多的内存。此外,在检查点保留器附加到表的节点上,每次更新的速度都会稍微慢一些。

对于每个表,可以选择是在该表的所有副本上附加一个检查点保留器,还是仅在一个副本上附加一个检查点保留器就足够了。如果每个表只有一个检查点保留器,则检查点会消耗更少的内存,但它容易受到节点崩溃的影响。如果有多个冗余检查点保留器,只要每个表至少附加一个活动的检查点保留器,检查点就可以继续存在。

可以使用函数 mnesia:deactivate_checkpoint(Name) 显式停用检查点,其中 Name 是活动检查点的名称。如果成功,此函数将返回 ok,如果发生错误,则返回 {error, Reason}。检查点中的所有表都必须至少附加到一个检查点保留器。当任何表缺少检查点保留器时,Mnesia 会自动停用该检查点。当节点关闭或删除副本时,可能会发生这种情况。使用参数 minmax(在以下列表中描述)来控制检查点保留器冗余的程度。

使用函数 mnesia:activate_checkpoint(Args) 激活检查点,其中 Args 是以下元组的列表

  • {name, Name},其中 Name 指定检查点的临时名称。停用检查点后,可以重用该名称。如果未指定名称,则会自动生成一个名称。
  • {max, MaxTabs},其中 MaxTabs 是要包含在检查点中的表列表。默认值为 [](空列表)。对于这些表,冗余最大化。当应用程序更新主表时,表的旧内容会保留在检查点保留器中。如果表有多个副本,则检查点的容错能力更强。当通过模式操作函数 mnesia:add_table_copy/3 添加新副本时,它还会附加一个本地检查点保留器。
  • {min, MinTabs},其中 MinTabs 是要包含在检查点中的表列表。默认值为 []。对于这些表,冗余最小化,并且每个表只有一个检查点保留器,最好在本地节点上。
  • {allow_remote, Bool},其中 false 表示所有检查点保留器必须是本地的。如果表不在本地,则无法激活检查点。true 允许在任何节点上分配检查点保留器。默认值为 true
  • {ram_overrides_dump, Bool}。此参数仅适用于类型为 ram_copies 的表。Bool 指定 RAM 中的表状态是否要覆盖磁盘上的表状态。true 表示 RAM 中最近提交的记录包含在检查点保留器中。这些是应用程序访问的记录。false 表示磁盘 .DAT 文件上的记录包含在检查点保留器中。这些记录在启动时加载。默认值为 false

函数 mnesia:activate_checkpoint(Args) 返回以下值之一

  • {ok, Name, Nodes}
  • {error, Reason}

Name 是检查点名称。Nodes 是已知该检查点的节点。

可以使用以下函数获取活动检查点的列表

启动文件、日志文件和数据文件

本节介绍 Mnesia 系统创建和维护的内部文件。特别是,介绍了 Mnesia 日志的工作原理。

启动文件

启动 Mnesia 说明了启动 Mnesia 的以下先决条件

  • 必须启动 Erlang 会话,并且必须为数据库指定 Mnesia 目录。
  • 必须使用函数 mnesia:create_schema/1 初始化数据库模式。

以下示例说明了如何执行这些任务

步骤 1: 启动 Erlang 会话,并为数据库指定 Mnesia 目录

% erl -sname klacke -mnesia dir '"/ldisc/scratch/klacke"'
Erlang (BEAM) emulator version 4.9

Eshell V4.9  (abort with ^G)
(klacke@gin)1> mnesia:create_schema([node()]).
ok
(klacke@gin)2>
^Z
Suspended

步骤 2: 您可以检查 Mnesia 目录,以查看创建了哪些文件

% ls -l /ldisc/scratch/klacke
-rw-rw-r--   1 klacke   staff       247 Aug 12 15:06 FALLBACK.BUP

响应显示已创建文件 FALLBACK.BUP。这称为备份文件,其中包含初始模式。如果在函数 mnesia:create_schema/1 中指定了多个节点,则将在所有节点上创建相同的备份文件。

步骤 3: 启动 Mnesia

(klacke@gin)3>mnesia:start( ).
ok

步骤 4: 您可以在 Mnesia 目录中看到以下列表

-rw-rw-r--   1 klacke   staff         86 May 26 19:03 LATEST.LOG
-rw-rw-r--   1 klacke   staff      34507 May 26 19:03 schema.DAT

备份文件 FALLBACK.BUP 中的模式已用于生成文件 schema.DAT。由于除了模式之外没有其他驻留在磁盘上的表,因此没有创建其他数据文件。文件 FALLBACK.BUP 在成功“还原”后被删除。您还可以看到一些供 Mnesia 内部使用的文件。

步骤 5: 创建表

(klacke@gin)4> mnesia:create_table(foo,[{disc_copies, [node()]}]).
{atomic,ok}

步骤 6: 您可以在 Mnesia 目录中看到以下列表

% ls -l /ldisc/scratch/klacke
-rw-rw-r-- 1 klacke staff    86 May 26 19:07 LATEST.LOG
-rw-rw-r-- 1 klacke staff    94 May 26 19:07 foo.DCD
-rw-rw-r-- 1 klacke staff  6679 May 26 19:07 schema.DAT

已创建文件 foo.DCD。此文件最终将存储写入 foo 表中的所有数据。

日志文件

启动 Mnesia 时,将创建名为 LATEST.LOG.LOG 文件,并将其放置在数据库目录中。Mnesia 使用此文件来记录基于磁盘的事务。这包括在存储类型为 disc_copiesdisc_only_copies 的表中至少写入一条记录的所有事务。该文件还包括操作模式本身的所有操作,例如创建新表。日志格式可能因 Mnesia 的不同实现而异。Mnesia 日志当前在 Kernel 的标准库模块 disk_log 中实现。

日志文件会持续增长,必须定期转储。“转储日志文件”是指 Mnesia 执行日志中列出的所有操作,并将记录放置到相应的 .DAT.DCD.DCL 数据文件中。例如,如果日志中列出了“写入记录 {foo, 4, elvis, 6}”的操作,Mnesia 会将该操作插入到 foo.DCL 文件中。稍后,当 Mnesia 认为 .DCL 文件太大时,数据会被移动到 .DCD 文件中。如果日志很大,转储操作可能会很耗时。请注意,在日志转储期间,Mnesia 系统会继续运行。

默认情况下,Mnesia 会在日志中写入 1000 条记录后或经过三分钟后转储日志。这由两个应用程序参数 -mnesia dump_log_write_threshold WriteOperations-mnesia dump_log_time_threshold MilliSecs 控制。

在转储日志之前,文件 LATEST.LOG 会被重命名为 PREVIOUS.LOG,并创建一个新的 LATEST.LOG 文件。日志成功转储后,文件 PREVIOUS.LOG 会被删除。

日志也会在启动时以及执行架构操作时转储。

数据文件

目录列表还包含一个 .DAT 文件,其中包含架构本身,位于 schema.DAT 文件中。DAT 文件是索引文件,可以高效地插入和搜索具有特定键的记录。 .DAT 文件用于架构和 disc_only_copies 表。Mnesia 数据文件目前在标准库模块 dets 中实现于 STDLIB 中。

可以在 dets 文件上执行的所有操作也可以在 Mnesia 数据文件上执行。例如,dets 包含函数 dets:traverse/2,可用于查看 Mnesia DAT 文件的内容。但是,这只能在 Mnesia 未运行时执行。因此,要查看架构文件,请执行以下操作:

{ok, N} = dets:open_file(schema, [{file, "./schema.DAT"},{repair,false},
{keypos, 2}]),
F = fun(X) -> io:format("~p~n", [X]), continue end,
dets:traverse(N, F),
dets:close(N).

警告

DAT 文件必须始终使用选项 {repair, false} 打开。这可确保这些文件不会自动修复。如果没有此选项,数据库可能会变得不一致,因为 Mnesia 可能认为文件已正确关闭。有关配置参数 auto_repair 的信息,请参阅参考手册。

警告

建议在 Mnesia 运行时不要篡改数据文件。虽然没有禁止,但 Mnesia 的行为是不可预测的。

disc_copies 表使用 .DCL.DCD 文件存储在磁盘上,这些文件是标准的 disk_log 文件。

启动时加载表

在启动时,Mnesia 加载表以使其可供其应用程序访问。有时 Mnesia 决定加载所有本地存在的表,有时表不可访问,直到 Mnesia 从另一个节点获取表的副本。

要理解 Mnesia 在启动时的行为,必须了解当 Mnesia 与另一个节点上的 Mnesia 失去联系时,Mnesia 如何反应。在此阶段,Mnesia 无法区分通信故障和“正常”的节点关闭。发生这种情况时,Mnesia 假设另一个节点不再运行,而实际上,节点之间的通信已失败。

为了克服这种情况,请尝试重新启动正在访问失败节点上的表的正在进行的事务,并将 mnesia_down 条目写入日志文件。

在启动时,请注意,所有驻留在没有 mnesia_down 条目的节点上的表都可能具有更新的副本。它们的副本可能在当前节点上的 Mnesia 终止后进行了更新。为了赶上最新的更新,请从这些其他“新鲜”节点之一传输表的副本。如果不幸,其他节点可能会关闭,您必须等待表在其中一个节点上加载后才能接收表的最新副本。

在应用程序首次访问表之前,必须执行 mnesia:wait_for_tables(TabList, Timeout) 以确保可以从本地节点访问该表。如果该函数超时,则应用程序可以选择使用 mnesia:force_load_table(Tab) 强制加载本地副本,并故意丢失在本地节点关闭时可能在其他节点上执行的所有更新。如果 Mnesia 已在另一个节点上加载表,或打算这样做,请从该节点复制表以避免不必要的不一致。

警告

mnesia:force_load_table(Tab) 仅加载一个表。由于已提交的事务可能已导致多个表中的更新,因此由于强制加载,这些表可能会变得不一致。

表的允许 AccessMode 可以定义为 read_onlyread_write。可以使用运行时函数 mnesia:change_table_access_mode(Tab, AccessMode) 进行切换。read_only 表和 local_content 表始终在本地加载,因为无需从其他节点复制表。如果表已加载到其他节点,或者正在运行的 Mnesia 已决定在那里加载该表,则其他表主要从其他节点上的活动副本远程加载。

在启动时,如果检测到以下任何一种情况,Mnesia 会假设其本地副本是最新的版本,并从磁盘加载表。

  • 从所有其他持有该表的磁盘驻留副本的节点返回 mnesia_down
  • 所有副本都是 ram_copies

这通常是一个明智的决定,但如果节点因通信故障而断开连接,则可能会是灾难性的,因为 Mnesia 的正常表加载机制无法应对通信故障。

Mnesia 加载许多表时,将使用默认加载顺序。但是,可以使用函数 mnesia:change_table_load_order(Tab, LoadOrder) 显式更改表的属性 load_order 来影响加载顺序。LoadOrder 对于所有表默认为 0,但可以设置为任何整数。具有最高 load_order 的表首先加载。更改加载顺序对于需要确保基本表尽早可用的应用程序尤其有用。大型外围表的加载顺序值应较低,可能小于 0

从通信故障中恢复

在某些情况下,Mnesia 可以检测到由于通信故障导致网络已分区,例如

  • Mnesia 已在运行,Erlang 节点重新获得联系。然后,Mnesia 会尝试联系另一个节点上的 Mnesia,以查看它是否也认为网络已分区一段时间。如果两个节点上的 Mnesia 都彼此记录了 mnesia_down 条目,则 Mnesia 会生成一个系统事件,称为 {inconsistent_database, running_partitioned_network, Node},该事件将发送到 Mnesia 事件处理程序和其他可能的订阅者。默认的事件处理程序会向错误记录器报告错误。
  • 如果 Mnesia 在启动时检测到本地节点和另一个节点都彼此接收到 mnesia_down,则 Mnesia 会生成一个 {inconsistent_database, starting_partitioned_network, Node} 系统事件,并按照上一个项目所述的方式操作。

如果应用程序检测到可能导致数据库不一致的通信故障,则可以使用函数 mnesia:set_master_nodes(Tab, Nodes) 来精确定位每个表可以从哪个节点加载。

在启动时,Mnesia 正常的表加载算法将被绕过,并且该表会从为该表定义的其中一个主节点加载,而不管日志中是否存在潜在的 mnesia_down 条目。Nodes 只能包含该表具有副本的节点。如果 Nodes 为空,则该特定表的主节点恢复机制将被重置,并且在下次重新启动时将使用正常的加载机制。

函数 mnesia:set_master_nodes(Nodes) 为所有表设置主节点。对于每个表,它确定其副本节点,并使用 Nodes 列表中包含的那些副本节点(即,TabNodesNodes 和该表的副本节点的交集)启动 mnesia:set_master_nodes(Tab, TabNodes)。如果交集为空,则特定表的主节点恢复机制将被重置,并且在下次重新启动时将使用正常的加载机制。

函数 mnesia:system_info(master_node_tables)mnesia:table_info(Tab, master_nodes) 可用于获取有关潜在主节点的信息。

确定在通信故障后保留哪些数据超出了 Mnesia 的范围。一种方法是确定哪个“岛屿”包含最多的节点。对关键表使用选项 {majority,true} 可以确保不属于“多数岛屿”的节点无法更新这些表。请注意,这会导致少数节点上的服务减少。这将是在更高的保证一致性方面的权衡。

函数 mnesia:force_load_table(Tab) 可用于强制加载表,而不管激活了哪种表加载机制。

事务恢复

Mnesia 表可以驻留在一个或多个节点上。当表被更新时,Mnesia 确保更新被复制到表驻留的所有节点。如果副本不可访问(例如,由于临时节点宕机),Mnesia 会稍后执行复制。

在应用程序启动的节点上,存在一个事务协调器进程。如果事务是分布式的,那么在需要执行提交工作的所有其他节点上,也存在一个事务参与者进程。

在内部,Mnesia 使用几种提交协议。选择的协议取决于事务中更新的表。如果所有涉及的表都是对称复制的(即,它们都具有相同的 ram_nodesdisc_nodesdisc_only_nodes,这些节点当前可以从协调器节点访问),则使用轻量级事务提交协议。

事务协调器及其参与者需要交换的消息数量很少,因为如果提交协议中断,Mnesia 表加载机制会负责事务恢复。由于所有涉及的表都是对称复制的,因此在故障节点启动时,通过从同一节点加载涉及的表来自动恢复事务。只要能确保 ACID 属性,事务是否已提交或终止并不重要。轻量级提交协议是非阻塞的,也就是说,即使任何节点在提交协议中间崩溃,幸存的参与者及其协调器也会完成事务。

如果节点在脏操作期间宕机,表加载机制会确保更新在所有副本上执行,或都不执行。异步脏更新和同步脏更新都使用与轻量级事务相同的恢复原则。

如果事务涉及非对称复制表的更新或模式表的更新,则使用重量级提交协议。此协议可以完成事务,而不管表的复制方式如何。重量级事务的典型用法是将副本从一个节点移动到另一个节点。然后确保副本要么完全移动,要么保持原样。永远不要出现副本同时存在于两个节点上或根本不存在任何节点上的情况。即使节点在提交协议中间崩溃,也必须保证事务是原子的。重量级提交协议比轻量级协议涉及事务协调器及其参与者之间更多的消息,并且它会在启动时执行恢复工作以完成终止或提交工作。

重量级提交协议也是非阻塞的,这允许幸存的参与者及其协调器完成事务,而不管(即使节点在提交协议中间崩溃)。当节点在启动时发生故障时,Mnesia 会确定事务的结果并恢复它。轻量级协议、重量级协议和脏更新都依赖于其他节点处于运行状态,以做出正确的重量级事务恢复决策。

如果 Mnesia 未在参与事务的某些节点上启动,并且本地节点和任何已运行的节点都不知道事务的结果,Mnesia 会默认等待一个节点。在最坏的情况下,所有其他涉及的节点都必须启动,Mnesia 才能做出关于事务的正确决策并完成其启动。

因此,如果发生双重故障,即两个节点同时崩溃,并且一个节点尝试启动而另一个节点由于硬件错误等原因拒绝启动,则 Mnesia(在一个节点上)可能会挂起。

可以指定 Mnesia 等待其他节点响应事务恢复决策的最大时间。配置参数 max_wait_for_decision 默认为 infinity,这可能会导致前面提到的无限期挂起。但是,如果将该参数设置为确定的时间段(例如,三分钟),则 Mnesia 会在必要时强制执行事务恢复决策,以允许 Mnesia 继续其启动过程。

强制执行事务恢复决策的缺点是,由于缺乏来自其他节点的恢复决策的充分信息,该决策可能不正确。这可能会导致数据库不一致,其中 Mnesia 在某些节点上提交了事务,但在其他节点上终止了事务。

在幸运的情况下,不一致性仅在属于特定应用程序的表中可见。但是,如果由于强制执行事务恢复决策而导致模式事务不一致地恢复,则不一致性的影响可能是致命的。但是,如果更高的优先级是可用性而不是一致性,那么冒这个风险可能是值得的。

如果 Mnesia 检测到不一致的事务决策,则会生成 {inconsistent_database, bad_decision, Node} 系统事件,以便应用程序有机会安装回退或其他适当的措施来解决不一致性。Mnesia 事件处理程序的默认行为与数据库因分区网络而变得不一致时的行为相同(如前所述)。

备份、恢复、回退和灾难恢复

以下函数用于备份数据、安装备份作为回退以及用于灾难恢复

这些函数将在以下部分中进行解释。另请参见 检查点,其中描述了用于激活和停用检查点的两个函数。

备份

备份操作通过以下函数执行

默认情况下,对备份介质的实际访问通过模块 mnesia_backup 执行,用于读取和写入。目前,mnesia_backup 是使用标准库模块 disk_log 实现的。但是,您可以编写具有与 mnesia_backup 相同接口的自己的模块,并配置 Mnesia,以便备用模块执行对备份介质的实际访问。因此,用户可以将备份放在 Mnesia 不知道的介质上,可能在没有运行 Erlang 的主机上。为此,请使用配置参数 -mnesia backup_module <module>

备份的来源是激活的检查点。备份函数 mnesia:backup_checkpoint(Name, Opaque,[Mod]) 最常用,并返回 ok{error,Reason}。它具有以下参数

  • Name 是已激活检查点的名称。有关如何在检查点中包含表名称的详细信息,请参见 检查点 中的函数 mnesia:activate_checkpoint(ArgList)
  • OpaqueMnesia 不解释此参数,但会将其转发到备份模块。Mnesia 默认备份模块 mnesia_backup 将此参数解释为本地文件名。
  • Mod 是备用备份模块的名称。

函数 mnesia:backup(Opaque [,Mod]) 激活一个新的检查点,该检查点涵盖所有具有最大冗余度的 Mnesia 表并执行备份。最大冗余意味着每个表副本都有一个检查点保留器。具有属性 local_contents 的表将按其在当前节点上的外观进行备份。

您可以迭代备份,要么将其转换为新备份,要么仅读取它。函数 mnesia:traverse_backup(Source, [SourceMod,] Target, [TargetMod,] Fun, Acc) 通常返回 {ok, LastAcc},用于这两种目的。

在遍历开始之前,源备份介质通过 SourceMod:open_read(Source) 打开,目标备份介质通过 TargetMod:open_write(Target) 打开。参数如下

  • SourceModTargetMod 是模块名称。
  • SourceTarget 是不透明数据,仅由模块 SourceModTargetMod 用于初始化备份介质。
  • Acc 是初始累加器值。
  • Fun(BackupItems, Acc) 应用于备份中的每个项目。Fun 必须返回一个元组 {ValGoodBackupItems, NewAcc},其中 ValidBackupItems 是有效备份项的列表。NewAcc 是一个新的累加器值。ValidBackupItems 使用函数 TargetMod:write/2 写入到目标备份中。
  • LastAcc 是最后的累加器值,也就是 Fun 返回的最后一个 NewAcc 值。

此外,可以对源备份执行只读遍历,而无需更新目标备份。如果 TargetMod==read_only,则不会访问目标备份。

通过将 SourceModTargetMod 设置为不同的模块,可以将备份从一个备份介质复制到另一个备份介质。

有效的 BackupItems 是以下元组

  • {schema, Tab} 指定要删除的表。
  • {schema, Tab, CreateList} 指定要创建的表。有关 CreateList 的更多信息,请参阅 mnesia:create_table/2
  • {Tab, Key} 指定要删除的记录的完整标识。
  • {Record} 指定要插入的记录。它可以是一个以 Tab 为第一个字段的元组。请注意,无论 record_name 设置为什么,记录名称都设置为表名称。

备份数据分为两个部分。第一部分包含与模式相关的信息。所有与模式相关的项目都是元组,其中第一个字段等于原子 schema。第二部分是记录部分。模式记录不能与其他记录混合,并且所有模式记录必须位于备份中的第一位。

模式本身是一个表,并且可能包含在备份中。模式表所在的每个节点都被视为一个 db_node

以下示例显示了如何使用 mnesia:traverse_backup 在备份文件中重命名 db_node

change_node_name(Mod, From, To, Source, Target) ->
    Switch =
        fun(Node) when Node == From -> To;
           (Node) when Node == To -> throw({error, already_exists});
           (Node) -> Node
        end,
    Convert =
        fun({schema, db_nodes, Nodes}, Acc) ->
                {[{schema, db_nodes, lists:map(Switch,Nodes)}], Acc};
           ({schema, version, Version}, Acc) ->
                {[{schema, version, Version}], Acc};
           ({schema, cookie, Cookie}, Acc) ->
                {[{schema, cookie, Cookie}], Acc};
           ({schema, Tab, CreateList}, Acc) ->
                Keys = [ram_copies, disc_copies, disc_only_copies],
                OptSwitch =
                    fun({Key, Val}) ->
                            case lists:member(Key, Keys) of
                                true -> {Key, lists:map(Switch, Val)};
                                false-> {Key, Val}
                            end
                    end,
                {[{schema, Tab, lists:map(OptSwitch, CreateList)}], Acc};
           (Other, Acc) ->
                {[Other], Acc}
        end,
    mnesia:traverse_backup(Source, Mod, Target, Mod, Convert, switched).

view(Source, Mod) ->
    View = fun(Item, Acc) ->
                   io:format("~p.~n",[Item]),
                   {[Item], Acc + 1}
           end,
    mnesia:traverse_backup(Source, Mod, dummy, read_only, View, 0).

恢复

可以在不重启 Mnesia 的情况下从备份在线恢复表。恢复通过函数 mnesia:restore(Opaque, Args) 执行,其中 Args 可以包含以下元组

  • {module,Mod}。备份模块 Mod 用于访问备份介质。如果省略,则使用默认备份模块。
  • {skip_tables, TableList},其中 TableList 是一个表列表,该列表不会从备份中读取。
  • {clear_tables, TableList},其中 TableList 是一个表列表,在插入备份中的记录之前要清除这些表。也就是说,在恢复表之前,会删除表中的所有记录。有关表的模式信息不会被清除或从备份中读取。
  • {keep_tables, TableList},其中 TableList 是一个表列表,在插入备份中的记录之前不会清除这些表。也就是说,备份中的记录会添加到表中的记录中。有关表的模式信息不会被清除或从备份中读取。
  • {recreate_tables, TableList},其中 TableList 是一个表列表,在插入备份中的记录之前要重新创建这些表。首先删除这些表,然后使用备份中的模式信息创建这些表。备份中的所有节点都需要处于运行状态。
  • {default_op, Operation},其中 Operationskip_tablesclear_tableskeep_tablesrecreate_tables 中的一个操作。默认操作指定要对备份中未在任何先前列表中指定的表使用的操作。如果省略,则使用操作 clear_tables

参数 Opaque 被转发到备份模块。如果成功,它将返回 {atomic, TabList},如果发生错误,则返回元组 {aborted, Reason}TabList 是已恢复表的列表。已恢复的表在恢复操作期间被写入锁定。但是,无论由此造成任何锁定冲突,应用程序都可以在恢复操作期间继续执行其工作。

恢复操作作为单个事务执行。如果数据库很大,则不能始终在线恢复。然后必须通过安装回退,然后重新启动来恢复旧数据库。

回退

函数 mnesia:install_fallback(Opaque, [Mod]) 将备份安装为回退。它使用备份模块 Mod 或默认备份模块来访问备份介质。如果成功,该函数将返回 ok,如果发生错误,则返回 {error, Reason}

安装回退是一个分布式操作,它*仅*在所有 db_nodes 上执行。回退会在下次系统启动时恢复数据库。如果安装了回退的 Mnesia 节点检测到另一个节点上的 Mnesia 已死亡,它将无条件地终止自身。

回退通常在执行系统升级时使用。系统通常涉及安装新的软件版本,并且 Mnesia 表通常会转换为新的布局。如果系统在升级期间崩溃,则很可能需要重新安装旧的应用程序,并将数据库恢复到其先前状态。如果在系统升级开始之前执行备份并将其安装为回退,则可以执行此操作。

如果系统升级失败,则必须在所有 db_nodes 上重新启动 Mnesia 以恢复旧数据库。回退会在成功启动后自动卸载。函数 mnesia:uninstall_fallback() 也可用于在系统升级成功后卸载回退。同样,这是一个分布式操作,它要么在所有 db_nodes 上执行,要么都不执行。安装和卸载回退都需要 Erlang 在所有 db_nodes 上运行,但 Mnesia 是否正在运行并不重要。

灾难恢复

由于电源故障,系统可能会变得不一致。UNIX 功能 fsck 可能会修复文件系统,但不能保证文件内容是一致的。

如果 Mnesia 检测到某个文件未正确关闭(可能是由于电源故障导致的),它会尝试以类似的方式修复坏文件。可能会丢失数据,但即使数据不一致,也可以重新启动 Mnesia。配置参数 -mnesia auto_repair <bool> 可用于控制 Mnesia 在启动时的行为。如果 <bool> 的值为 true,则 Mnesia 会尝试修复该文件。如果 <bool> 的值为 false,则如果 Mnesia 检测到可疑文件,则不会重新启动。此配置参数会影响日志文件、DAT 文件和默认备份介质的修复行为。

配置参数 -mnesia dump_log_update_in_place <bool> 控制函数 mnesia:dump_log() 的安全级别。默认情况下,Mnesia 将事务日志直接转储到 DAT 文件中。如果在转储期间发生电源故障,这可能会导致随机访问的 DAT 文件损坏。如果该参数设置为 false,则 Mnesia 会复制 DAT 文件,并将转储定向到新的临时文件。如果转储成功,则临时文件将重命名为其正常的 DAT 后缀。使用此策略,数据文件中出现不可恢复的不一致性的可能性会大大降低。但是,事务日志的实际转储速度会变得慢得多。系统设计人员必须决定速度还是安全是更高的优先级。

类型为 disc_only_copies 的副本仅在启动时日志文件的初始转储期间受此参数影响。在设计具有*非常*高要求的应用程序时,完全不使用 disc_only_copies 表可能是合适的。原因是正常操作系统文件的随机访问性质。如果节点因电源故障等原因而关闭,这些文件可能会损坏,因为它们没有正确关闭。 disc_only_copiesDAT 文件会根据每个事务进行更新。

如果发生灾难并且 Mnesia 数据库损坏,则可以从备份中重建它。将此视为最后的手段,因为备份包含旧数据。数据希望是一致的,但是当使用旧备份来恢复数据库时,数据肯定会丢失。