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

davidgao · 2017年12月19日 · 6308 次阅读

FALLBACK.BUP 生成 schema.DAT 时机

前面的文章提到了如何生成 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。

恢复流程

tm_fallback_start 函数

在 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 函数会进行数据恢复的准备工作,它进行了下面这些工作

  • 建立 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 的启动和读写过程后,将会逐步对相关机制展开分析。

转载自TechTalk

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号