深入浅出 Mnesia-schema 创建 (2)

FALLBACK.BUP生成schema.DAT时机

前面的文章提到了如何生成FALLBACK.BUP,但没有提到FALLBACK.BUP是怎样生成schema.DAT文件的。想要知道FALLBACK.BUP是如何生成schema.DAT,就需要去观察Mnesia的启动流程和监控树。

通过对代码的分析,可以非常清晰的看到,Mnesia的主要进程都被mnesia_kernel_sup这个监控者进程下 ``` Erlang 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。

## 恢复流程

### tm_fallback_start函数
在mnesia_bup:tm_fallback_start函数中,整个操作过程都是锁住schema表进行操作的,在这个过程中,本节点上所有的其它schema操作都会进行等待。同时,这个函数的整体操作都是在mnesia_tm进程内进行的。
``` Erlang
%执行回滚操作
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函数会进行数据恢复的准备工作,它进行了下面这些工作 - 建立mnesia_local_tables的ets表,用来保存restore_tables函数在恢复过程中,恢复出本节点内的表的信息 - 生成schema.DAT文件 - 生成本节点内所有表的.DAT文件,.DCL文件和.DCD文件

restore_tables函数

restore_tables函数依旧是依赖mnesia_bup的通用函数iterate,将FALLBACK.BUP文件中的schema数据和表项目数据读取出来,并逐条遍历。

restore_tables函数会有几个状态,这几个状态分别是 - {start, LocalTabs},从FALLBACK.BUP中读取schema信息,根据表信息构建local_tab这个record,并保存到mnesia_local_tables中,为了可以在后面的恢复操作中使用 - {new, LocalTabs} ,开始各表的数据恢复,会对FALLBACK中的数据项的record名和mnesia_local_tables的table名称进行比对,从而决定是恢复数据还是忽略 - {not_local, LocalTabs, Tab},如果在mnesia_local_tables查找不到对应的表名称的时候,就会进入此状态,这个过程中读取的数据会全部忽略掉 - {local, LocalTabs, LT},在mnesia_local_tables中查找到对应表明,进入此状态,进行数据恢复

init_dat_files函数

init_dat_files是restore_tables在构建mnesia_local_tables中项目的重要函数。

它的主要工作有: - 根据schema提供的信息生成local_tab的record,包括schema表自身的信息 - 创建除了schema表外所有的表的数据文件

在仔细观察这个函数会发现,针对存储类型为disc_only_copies的表会建立一个dets,而对ram_copies和disc_copies类型存储的表只会建立.DCL日志存储和.DCD存储。存储方式的不同直接影响到了Mnesia如何读取数据和管理数据,在后面的文章将逐步分析相关内容。

总结

从整个过程中,可以看到Mnesia创建schema的过程后半段不仅仅可以创建一个schema.DAT文件,而且能创建包含数据的表。同时可以看出,Mnesia的Schema.DAT就是一个dets文件,完全可以简单的创建出来,但是这个过程确大费周章。当然这样做是有很大的原因的,这就需要考察Mnesia的备份机制和远程安装数据的机制了,在分析完Mnesia的启动和读写过程后,将会逐步对相关机制展开分析。

Power by Vultr and AiWiki
Copyright © 2019 David Fox All Rights Reserved.