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 表里指定键的对象数据的第几个元素数据
网友评论