[Erlang开发之路]十九、用ets和dets储存数据

ETS和DETS的介绍

ETS和DETS都是两个系统模块,可以用来储存海量的数据,ETS的全称是Erlang Term Storage(erlang 数据储存),而Dets就是加了个Disk,磁盘ETS,他们的任务相同,提供大型的KV(Key-Value)查询表,ETS比DETS高效,它储存在内存中,

一、ETS

表的类型

首先分为两个大类:

  • 异键表(set):要求键是唯一的
    • 异键表
    • 有序异键表(ordered set):元组会被排序
  • 同键表(bag):允许多个元素使用同一个键,但不能允许有键-值完全相同的元素存在
    • 同键表
    • 副本同键(duplicate bag)

表的操作

1.创建一个ETS表

-spec ets:new(TableName,[Option])-> TabeId when
TableName::atom().
Option:: [set | ordered_set | bag | duplicate bag,private | public | protected,named_table,{keypos,Pos}].
%% set | ordered_set | bag | duplicate bag 是表的类型上面有总结
%% private是创建一个私有表,只有主管进程(创建表的进程)才可以读写它
%% public是创建一个公共表,只要知道TableId就可以读写它
%% protected是创建一个受保护表,只有主管进程可以写它,其他进程只可以读
%% named_table 设置了这个选项之后,可以用TableName代替TableId对表进行操作
%% {keypos,Pos}以元组的Pos位置作为键所在的位置,通常为1
%% 当Option是一个空列表,采用默认值: [set,protected,{keypos,1}]

2.表的写入

-spec ets:insert(TableId,[tuple()])-> bool() when
%% TableId是表的标识符
%% [tuple()]是以拥有一个以上元素的元组为元素的列表,[{key1,V1},{key2,V2}],可以向表写入多个Key-Value
%% 举例:
ets:insert(TableId,[{key1,123,{test,666}},{key2,456,abc}]).

表对键是否相同的判断

这里要先介绍两种判断方法:

  • match
    match相当于精确判断,不仅值要相同,类型也要相同,例如1.0和1 虽然值相同,但是1.0是float类型 而1是integer类型

  • compare equal
    compare equal相当于模糊判断,值相同即可

    Set类型的表采用match来判断键是否重复,ordered_set类型的表采用compare equal来判断

3.表的读取

-spec ets:lookup(TabId,Key)->[tuple()]
%% 返回一组符合要求的元组结果

除了使用lookup来查找,ets还提供了一些更为强大的查找方法:

-spec ets:match(TabId, Pattern) -> [Match] 
%% Match可以是tuple中某些元素,也可以是全部元素
%% 举例
test()->
    TableId=ets:new(etsTable,[bag]),
    ets:insert(etsTable,{key,value1,value2}),
    io:format("matchResult:~p~n",[getMatch(TableId,{key,_,'$1'})]),
    io:format("matchResult:~p~n",[getMatch(TableId,{key,'$2','$1'})]),
    io:format("matchResult:~p~n",[getMatch(TableId,'$1')]).
getMatch(TabId,Pattern)->
    ets:match(TabId,Pattern).
%% 输出结果就是
%% [[value2]]
%% [[value2,value1]]

其中Pattern可以包含以下字符 1.一个绑定变量或者任意Erlang Term;
2.一个占位符'_',可以匹配任何Erlang Term;
3.一个变量符'$N' (N可以为0,1,2,3....),该变量指定了match方法需要返回tuple中哪些元素;
若Pattern中包含Key值,那么查找起来非常快速(相当于索引查询),否则需要全表遍历

我们除了可以一次性查找出所有满足匹配的tuples,也可以采用"分页查询"的方式查询,即每次只查询出部分结果,然后通过迭代查找出所有结果:

-spec ets:match(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'
%% Continuation变量保存了下一个分页的信息,要获取下一页时,只需要match(Continuation)即可获取下一页,当返回'$end_of_table'时没有下一页了
%% 例子:
test()->
    TableId=ets:new(etsTable,[bag]),
    ets:insert(etsTable,{key1,value1,value2}),
    ets:insert(etsTable,{key1,value2,value1}),
    {FirstMatch,Page2}=ets:match(TableId,{key1,'$1','$2'},1),%%一页一条记录
    io:format("matchResult:~p~n",[FirstMatch])),
    {SecondMatch,Page3}=ets:match(Page2),%%一页一条记录
    io:format("matchResult:~p~n",[SecondMatch])),
    Page3. 
%% 输出结果:
%% matchResult:[[value2],[value1]]
%% matchResult:[[value1],[value2]]
%% '$end_of_table'

4.最后我们来看下ets table的遍历

-spec first(TableId) -> Key | '$end_of_table' %%用于获取表中第一个Key
-spec next(TableId, Key1) -> Key2 | '$end_of_table' %%用于获取Key1后面的Key
%%来个遍历获取{key,Value}的例子
listTable(TableId)->
    case ets:first(TableId) of
        '$end_of_table'->
            io:format("啥都没有让我遍历个啥~n");
        Key->
            Result=ets:lookup(TableId,Key),
            io:format("Key: ~p Value:~p ~n",[Key,Result]),
            listTable(TableId,Key).
listTable(TableId,Key)->
    case ets:next(TableId,Key) of
        '$end_of_table'->
            io:format("到尾了兄弟~n");
        Key2->
            Result=ets:lookup(TableId,Key),
            io:format("Key: ~p Value:~p ~n",[Key2,Result]),
            listTable(TableId,Key2).

ets的其他函数

-spec delete(Tab, Key) -> true
%删除表中某个KV
-spec delete(Tab) -> true
%删除整个表
-spec all() -> [Tab]
%获取目前所有表
-spec delete_all_objects(Tab) - > true
%删除ETS表选项卡中的所有对象。该操作保证是 原子的和隔离的。
-spec delete_object(Tab,Object) - > true
%从ETS表中删除确切的对象Object,保留具有相同键但具有其他差异的对象(对于类型包有用)。在duplicate_bag表中,将删除该对象的所有实例。
-spec file2tab(FileSrc) - > {ok,Tab} | {error,Reason}
%读取tab2file / 2或 tab2file / 3生成的文件,并创建相应的表Tab。相当于file2tab(Filename,[])。
-spec tab2file(Tab, Filename) -> ok | {error, Reason}
%保存table到File
%....后面太多了 看手册去吧

二、DETS

ETS把元组保存在内存里,而DETS把元组保存在磁盘上,它的最大文件大小是2GB,DETS文件必须先打开才可以操作,用完以后还要正确关闭。他们之间表的属性也不一样,DETS在打开文件时必须赋予一个全局名称,如果两个或者更多的进程以同一个名称和选项打开了文件,他们就会共享这个表,直到所有进程都关闭他,否则一直是打开状态

%拿例子说话
open(File)->
    io:format("dets opened:~p~n",[File]),
    Bool = filelib:is_file(File),
    case dets:open_file(?MODULE,[{file,File}]) of
        {ok,?MODULE}->
            case Bool of
                true->void;
                false->ok=dets:insert(?MODULE,{free,1})
            end,
            true;
       {error,Reason}->
            io:format("cannot open dets table~n"),
            exit({eDetsOpen,File,Reason})
     end.
 close()->
    dets:close(?MODULE).
    
%这个例子是dets打开和关闭的例子,我们使用了模块名作为表名
filename2index(FileName) when is_binary(FileName)->
    case dets:lookup(?MODULE,Filename) of
        []->
            [{_,Free}]=dets:lookup(?MODULE,free),
            ok=dets:insert(?MODULE,[{Free,FileName},{FileName,Free},{free,Free+1}]),
            Free;
        [{_,N}]->
            N
     end.
 %这个例子就包含了读和写

手册内推荐阅读有关ETS和DETS的函数

  • 基于模式获取和删除对象
  • ETS和DETS、以及ETS表和文件之间的互相转换
  • 查看表的资源占用情况
  • 遍历表内所有的元素
  • 修复损坏的DETS表
  • 让表可视化

ETS表的各种方法:
ets:all/0
获取所有的 ets 表

ets:delete/1
删除整张表

ets:delete/2
删除表里指定键的所有数据

ets:delete_all_objects/1
删除表里的所有数据

ets:delete_object/2
删除表里的指定数据

ets:file2tab/1
从一个文件读取一个 ETS 表

ets:first/1
获取 ETS 表里的第一个对象数据的键

ets:foldl/3
对 ETS 数据遍历循环操作

ets:fun2ms/1
把语法函数转为匹配规范的伪函数

ets:give_away/3
改变一个表的拥有者

ets:i/0
在输出端上打印显示所有 ETS 表的信息

ets:info/1
返回一个 ETS 表的信息

ets:info/2
返回给出的跟表相关的项的信息

ets:insert/2
向 ETS 表插入数据

ets:insert_new/2
向 ETS 表插入新数据

ets:is_compiled_ms/1
检测一个 Erlang 数据是否是一个有效已编译的匹配规范

ets:last/1
返回表里的最后一个键

ets:lookup/2
在 ETS 表里查出相应键的值

ets:lookup_element/3
返回 ETS 表里指定键的对象数据的第几个元素数据

ets:match/1 | ets:match/2 | ets:match/3
根据匹配模式匹配表里的对象数据

ets:match_delete/2 | ets:match_object/1 | ets:match_object/2 | ets:match_object/3
根据匹配模式删除表里的对象数据

ets:match_spec_compile/1
把一个匹配规范编译为它的内部表示形式

ets:match_spec_run/2
使用一个匹配规范来执行匹配操作

ets:member/2
判断表里面是否存在指定键的数据

ets:new/2
创建一个 ets 表

ets:next/2
返回表的下一个键

ets:prev/2
返回表的上一个键

ets:rename/2
重新给 ETS 表命名一个名字

ets:safe_fixtable/2
锁定一定 ETS 表使其可以安全遍历

ets:select/1
对 ETS 表里的数据进行匹配比对

ets:select_delete/2
根据匹配模式删除表里的对象数据

ets:tab2file/2 | ets:tab2file/2
把一个 ETS 表转储到一个文件里

ets:tab2list/1
返回一个 ETS 表的所有对象数据的列表

ets:to_dets/2
把内存里的 ETS 数据插入到磁盘上保存

ets:update_element/3
更新 ETS 表里指定键的对象数据的第几个元素数据

你可能感兴趣的:([Erlang开发之路]十九、用ets和dets储存数据)