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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356