前面的文章提到了如何生成 FALLBACK.BUP,但没有提到 FALLBACK.BUP 是怎样生成 schema.DAT 文件的。想要知道 FALLBACK.BUP 是如何生成 schema.DAT,就需要去观察 Mnesia 的启动流程和监控树。
通过对代码的分析,可以非常清晰的看到,Mnesia 的主要进程都被 mnesia_kernel_sup 这个监控者进程下
init([]) ->
ProcLib = [mnesia_monitor, proc_lib],
Flags = {one_for_all, 0, timer:hours(24)}, % Trust the top supervisor
%% 最先启动的是mnesia_monitor
%% mnesia_monitor持有mnesia_gvar和mnesia_stats两张ets表
%% mnesia的全局变量全都保存在此处
Workers = [worker_spec(mnesia_monitor, timer:seconds(3), [gen_server]),
%% mnesia_subscr 创建订阅管理进程
%% 自动将mnesia_event加入到系统订阅表中
worker_spec(mnesia_subscr, timer:seconds(3), [gen_server]),
%% mnesia的锁管理进程
worker_spec(mnesia_locker, timer:seconds(3), ProcLib),
%% mnesia恢复进程
worker_spec(mnesia_recover, timer:minutes(3), [gen_server]),
%% mnesia事务进程
worker_spec(mnesia_tm, timer:seconds(30), ProcLib),
%% 检察点监控者进程
supervisor_spec(mnesia_checkpoint_sup),
%% snmp监控者进程
supervisor_spec(mnesia_snmp_sup),
%% mnesia主控进程
worker_spec(mnesia_controller, timer:seconds(3), [gen_server]),
%% mnesia数据加载进程
worker_spec(mnesia_late_loader, timer:seconds(3), ProcLib)
],
{ok, {Flags, Workers}}.
通过逐个进程的检察,在 mnesia_tm 进程初始化的时候,会通过 mnesia_bup:tm_fallback_start 函数来使用 FALLBACK.BUP 进行数据恢复,在此过程中就会生成 schema.DAT。
在 mnesia_bup:tm_fallback_start 函数中,整个操作过程都是锁住 schema 表进行操作的,在这个过程中,本节点上所有的其它 schema 操作都会进行等待。同时,这个函数的整体操作都是在 mnesia_tm 进程内进行的。
%执行回滚操作
do_fallback_start(true, false) ->
verbose("Starting from fallback...~n", []),
%拿到备份文件
BupFile = fallback_bup(),
Mod = mnesia_backup,
%创建一个ets,用来保存本地表
LocalTabs = ?ets_new_table(mnesia_local_tables, [set, public, {keypos, 2}]),
case catch iterate(Mod, fun restore_tables/4, BupFile, {start, LocalTabs}) of
{ok, _Res} ->
%% 让dets关闭掉schema
catch dets:close(schema),
%% 设置临时的文件为schema.TMP
TmpSchema = mnesia_lib:tab2tmp(schema),
%% 设置数据文件为schema.DAT
DatSchema = mnesia_lib:tab2dat(schema),
%% 得到所有本地表
AllLT = ?ets_match_object(LocalTabs, '_'),
%关闭ets
?ets_delete_table(LocalTabs),
%% schema.TMP重命名为schema.DAT
case file:rename(TmpSchema, DatSchema) of
ok ->
%% 除了schema表外,全部进行swap操作
[(LT#local_tab.swap)(LT#local_tab.name, LT) ||
LT <- AllLT, LT#local_tab.name =/= schema],
file:delete(BupFile),
ok;
{error, Reason} ->
file:delete(TmpSchema),
{error, {"Cannot start from fallback. Rename error.", Reason}}
end;
{error, Reason} ->
{error, {"Cannot start from fallback", Reason}};
{'EXIT', Reason} ->
{error, {"Cannot start from fallback", Reason}}
end.
do_fallback_start 函数会进行数据恢复的准备工作,它进行了下面这些工作
restore_tables 函数依旧是依赖 mnesia_bup 的通用函数 iterate,将 FALLBACK.BUP 文件中的 schema 数据和表项目数据读取出来,并逐条遍历。
restore_tables 函数会有几个状态,这几个状态分别是
init_dat_files 是 restore_tables 在构建 mnesia_local_tables 中项目的重要函数。
它的主要工作有:
在仔细观察这个函数会发现,针对存储类型为 disc_only_copies 的表会建立一个 dets,而对 ram_copies 和 disc_copies 类型存储的表只会建立.DCL 日志存储和.DCD 存储。存储方式的不同直接影响到了 Mnesia 如何读取数据和管理数据,在后面的文章将逐步分析相关内容。
从整个过程中,可以看到 Mnesia 创建 schema 的过程后半段不仅仅可以创建一个 schema.DAT 文件,而且能创建包含数据的表。同时可以看出,Mnesia 的 Schema.DAT 就是一个 dets 文件,完全可以简单的创建出来,但是这个过程确大费周章。当然这样做是有很大的原因的,这就需要考察 Mnesia 的备份机制和远程安装数据的机制了,在分析完 Mnesia 的启动和读写过程后,将会逐步对相关机制展开分析。
转载自TechTalk