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

davidgao · December 19, 2017 · 6297 hits

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

No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.