2019独角兽企业重金招聘Python工程师标准>>>
在《mnesia之transaction》里提到事务操作的数据及最终结果都会记录到latest.log文件中。注意只有涉及类型为disc_copies和disc_only_copies的表的操作才会记录日志到latest.log文件中,仅针对ram_copies类型的表的操作不会记录日志。
log(C) when C#commit.disc_copies == [],C#commit.disc_only_copies == [],C#commit.schema_ops == [] ->ignore;
为了防止日志文件不断增长从而导致占用大量的磁盘空间,mnesia会进行dump工作。所谓dump就是分析日志文件中记录的事务操作及最终结果,将实际的数据记录到*.DAT,*.DCL,*.DCD等文件中。
说明:mnesia数据存储实际上使用的是ets和dets,对于ram_copies类型的表使用ets;disc_copies类型的表使用也是ets,通过mnesia的dump将数据保存到后缀名为DCD(disc copy data)或者后缀名为DCL(disc copy log)的文件中,以做到数据的持久化;而disc_only_copies类型的表使用的是dets,保存的文件后缀名为DAT;schema表比较特殊,虽然使用的是dets表,但是同时会在内存中保存相关信息。
有几种情况会触发mnesia的dump:
(1)定时触发
mnesia启动后,由mnesia_controller进程设置定时器,触发dump
代码片段:
init([Parent]) ->process_flag(trap_exit, true),mnesia_lib:verbose("~p starting: ~p~n", [?SERVER_NAME, self()]),All = mnesia_lib:all_nodes(),Diff = All -- [node() | val(original_nodes)],mnesia_lib:unset(original_nodes),mnesia_recover:connect_nodes(Diff),Ref = next_async_dump_log(),mnesia_dumper:start_regulator(),Empty = gb_trees:empty(),{ok, #state{supervisor = Parent,dump_log_timer_ref = Ref,loader_queue = Empty,late_loader_queue = Empty}}.next_async_dump_log() ->Interval = mnesia_monitor:get_env(dump_log_time_threshold),Msg = {next_async_dump_log, time_threshold},Ref = erlang:send_after(Interval, self(), Msg),Ref.handle_info({next_async_dump_log, InitBy}, State) ->async_dump_log(InitBy),Ref = next_async_dump_log(),noreply(State#state{dump_log_timer_ref=Ref});
定时dump的默认的时间间隔为3分钟
可以在程序启动是增加参数 -mnesia dump_log_time_threshold 300000 来设置时间间隔。 default_env(dump_log_time_threshold) ->timer:minutes(3);
(2)一定数量的日志记录后触发
每次调用mnesia_log:log(C)或者mnesia_log:slog(C)进行日志记录时,都会将trans_log_writes_left的值减1,当该值小于等于0时,触发dump
默认情况下,写入100条记录到latest文件中,便会触发dump mnesia_log:
log(C) ->case mnesia_monitor:use_dir() oftrue ->...mnesia_dumper:incr_log_writes();false ->ignoreend.mnesia_dumper:
incr_log_writes() ->Left = mnesia_lib:incr_counter(trans_log_writes_left, -1),ifLeft > 0 ->ignore;true ->adjust_log_writes(true)end.adjust_log_writes(DoCast) ->...case DoCast offalse ->ignore;true ->mnesia_controller:async_dump_log(write_threshold)end,...
mnesia_monitor:
init(Parent) ->...Left = get_env(dump_log_write_threshold),mnesia_lib:set_counter(trans_log_writes_left, Left),...
同样可以通过在程序启动是增加参数 -mnesia dump_log_write_threshold 5000 进行设置。
dump操作会做如下几个事情:
(1) dump latest.log文件
将latest.log文件改名为previous.log,然后新建latest.log文件,然后分析previous.log文件中的内容,对于存储类型为disc_copies的表(非schema),检查DCL与DCD文件中的数据量,当sizeof(DCD)/sizeof(DCL)小于指定的阈值时,把表中的内容全部存储到DCD文件中,否则直接写到DCL文件中。默认的阈值大小为4,可以通过-mnesia dc_dump_limit Num进行设置。对于存储类型为disc_only_copies的表不做任何处理。
open_disc_copies(Tab, InitBy) ->DclF &#61; mnesia_lib:tab2dcl(Tab),DumpEts &#61;case file:read_file_info(DclF) of{error, enoent} ->false;{ok, DclInfo} ->DcdF &#61; mnesia_lib:tab2dcd(Tab),case file:read_file_info(DcdF) of{error, Reason} ->mnesia_lib:dbg_out("File ~p info_error ~p ~n",[DcdF, Reason]),true;{ok, DcdInfo} ->Mul &#61; case ?catch_val(dc_dump_limit) of{&#39;EXIT&#39;, _} -> ?DumpToEtsMultiplier;Val -> Valend,DcdInfo#file_info.size &#61;<(DclInfo#file_info.size * Mul)endend,ifDumpEts &#61;&#61; false; InitBy &#61;&#61; startup ->mnesia_log:open_log({?MODULE,Tab},mnesia_log:dcl_log_header(),DclF,mnesia_lib:exists(DclF),mnesia_monitor:get_env(auto_repair),read_write),put({?MODULE, Tab}, {opened_dumper, dcl}),true;true ->mnesia_log:ets2dcd(Tab),put({?MODULE, Tab}, already_dumped),falseend.
&#xff08;2&#xff09;dump mnesia_decision表
将当前mnesia_decision表中的数据以覆盖形式存储到decision_tab.log文件中
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
另外&#xff0c;mnesia启动和执行schema transaction时都会触发dump&#xff0c;schema transaction触发的dump会忽略日志文件中的内容&#xff0c;也不会对mnesia_decision表进行dump&#xff0c;仅针对本次操作涉及的内容进行dump&#xff0c;并且schema transaction触发的dump不会和其他情况触发的dump并行执行。
prepare_commit时锁住&#xff1a;
do_commit时进行dump&#xff0c;然后释放锁 mnesia_schema:prepare_commit(Tid, Commit, WaitFor) ->...case Ops of[] ->ignore;_ ->%% We need to grab a dumper lock here, the log may not%% be dumped by others, during the schema commit phase.mnesia_controller:wait_for_schema_commit_lock()end...
mnesia_tm:do_commit(Tid, C, DumperMode) ->mnesia_dumper:update(Tid, C#commit.schema_ops, DumperMode),...mnesia_dumper:update(Tid, SchemaOps, DumperMode) ->UseDir &#61; mnesia_monitor:use_dir(),Res &#61; perform_update(Tid, SchemaOps, DumperMode, UseDir),mnesia_controller:release_schema_commit_lock(),Res.