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

Erlang的Mnesia数据库如何创建schema

FALLBACK.BUP生成schema.DAT时机

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

通过对代码的分析,可以非常清晰的看到,Mnesia的主要进程都被mnesia_kernel_sup这个 监控者进程下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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进程内进行的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
%执行回滚操作
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的启动和读写过程后, 将会逐步对相关机制展开分析。