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