[Erlang開發(fā)之路]十九、用ets和dets儲(chǔ)存數(shù)據(jù)

ETS和DETS的介紹

ETS和DETS都是兩個(gè)系統(tǒng)模塊胰蝠,可以用來(lái)儲(chǔ)存海量的數(shù)據(jù)歼培,ETS的全稱是Erlang Term Storage(erlang 數(shù)據(jù)儲(chǔ)存),而Dets就是加了個(gè)Disk,磁盤ETS茸塞,他們的任務(wù)相同躲庄,提供大型的KV(Key-Value)查詢表,ETS比DETS高效钾虐,它儲(chǔ)存在內(nèi)存中噪窘,

一、ETS

表的類型

首先分為兩個(gè)大類:

  • 異鍵表(set):要求鍵是唯一的
    • 異鍵表
    • 有序異鍵表(ordered set):元組會(huì)被排序
  • 同鍵表(bag):允許多個(gè)元素使用同一個(gè)鍵效扫,但不能允許有鍵-值完全相同的元素存在
    • 同鍵表
    • 副本同鍵(duplicate bag)

表的操作

1.創(chuàng)建一個(gè)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 是表的類型上面有總結(jié)
%% private是創(chuàng)建一個(gè)私有表倔监,只有主管進(jìn)程(創(chuàng)建表的進(jìn)程)才可以讀寫它
%% public是創(chuàng)建一個(gè)公共表,只要知道TableId就可以讀寫它
%% protected是創(chuàng)建一個(gè)受保護(hù)表菌仁,只有主管進(jìn)程可以寫它浩习,其他進(jìn)程只可以讀
%% named_table 設(shè)置了這個(gè)選項(xiàng)之后,可以用TableName代替TableId對(duì)表進(jìn)行操作
%% {keypos,Pos}以元組的Pos位置作為鍵所在的位置济丘,通常為1
%% 當(dāng)Option是一個(gè)空列表谱秽,采用默認(rèn)值: [set,protected,{keypos,1}]

2.表的寫入

-spec ets:insert(TableId,[tuple()])-> bool() when
%% TableId是表的標(biāo)識(shí)符
%% [tuple()]是以擁有一個(gè)以上元素的元組為元素的列表洽蛀,[{key1,V1},{key2,V2}],可以向表寫入多個(gè)Key-Value
%% 舉例:
ets:insert(TableId,[{key1,123,{test,666}},{key2,456,abc}]).

表對(duì)鍵是否相同的判斷

這里要先介紹兩種判斷方法:

  • match
    match相當(dāng)于精確判斷,不僅值要相同疟赊,類型也要相同郊供,例如1.0和1 雖然值相同,但是1.0是float類型 而1是integer類型

  • compare equal
    compare equal相當(dāng)于模糊判斷近哟,值相同即可

    Set類型的表采用match來(lái)判斷鍵是否重復(fù)驮审,ordered_set類型的表采用compare equal來(lái)判斷

3.表的讀取

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

除了使用lookup來(lái)查找,ets還提供了一些更為強(qiáng)大的查找方法:

-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).
%% 輸出結(jié)果就是
%% [[value2]]
%% [[value2,value1]]

其中Pattern可以包含以下字符 1.一個(gè)綁定變量或者任意Erlang Term头岔;
2.一個(gè)占位符'_',可以匹配任何Erlang Term鼠证;
3.一個(gè)變量符'$N' (N可以為0,1,2,3....),該變量指定了match方法需要返回tuple中哪些元素靠抑;
若Pattern中包含Key值量九,那么查找起來(lái)非常快速(相當(dāng)于索引查詢)颂碧,否則需要全表遍歷

我們除了可以一次性查找出所有滿足匹配的tuples荠列,也可以采用"分頁(yè)查詢"的方式查詢,即每次只查詢出部分結(jié)果载城,然后通過迭代查找出所有結(jié)果:

-spec ets:match(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'
%% Continuation變量保存了下一個(gè)分頁(yè)的信息肌似,要獲取下一頁(yè)時(shí),只需要match(Continuation)即可獲取下一頁(yè)诉瓦,當(dāng)返回'$end_of_table'時(shí)沒有下一頁(yè)了
%% 例子:
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),%%一頁(yè)一條記錄
    io:format("matchResult:~p~n",[FirstMatch])),
    {SecondMatch,Page3}=ets:match(Page2),%%一頁(yè)一條記錄
    io:format("matchResult:~p~n",[SecondMatch])),
    Page3. 
%% 輸出結(jié)果:
%% matchResult:[[value2],[value1]]
%% matchResult:[[value1],[value2]]
%% '$end_of_table'

4.最后我們來(lái)看下ets table的遍歷

-spec first(TableId) -> Key | '$end_of_table' %%用于獲取表中第一個(gè)Key
-spec next(TableId, Key1) -> Key2 | '$end_of_table' %%用于獲取Key1后面的Key
%%來(lái)個(gè)遍歷獲取{key,Value}的例子
listTable(TableId)->
    case ets:first(TableId) of
        '$end_of_table'->
            io:format("啥都沒有讓我遍歷個(gè)啥~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的其他函數(shù)

-spec delete(Tab, Key) -> true
%刪除表中某個(gè)KV
-spec delete(Tab) -> true
%刪除整個(gè)表
-spec all() -> [Tab]
%獲取目前所有表
-spec delete_all_objects(Tab) - > true
%刪除ETS表選項(xiàng)卡中的所有對(duì)象川队。該操作保證是 原子的和隔離的。
-spec delete_object(Tab睬澡,Object) - > true
%從ETS表中刪除確切的對(duì)象Object固额,保留具有相同鍵但具有其他差異的對(duì)象(對(duì)于類型包有用)。在duplicate_bag表中煞聪,將刪除該對(duì)象的所有實(shí)例斗躏。
-spec file2tab(FileSrc) - > {ok,Tab} | {error昔脯,Reason}
%讀取tab2file / 2或 tab2file / 3生成的文件啄糙,并創(chuàng)建相應(yīng)的表Tab。相當(dāng)于file2tab(Filename云稚,[])隧饼。
-spec tab2file(Tab, Filename) -> ok | {error, Reason}
%保存table到File
%....后面太多了 看手冊(cè)去吧

二、DETS

ETS把元組保存在內(nèi)存里碱鳞,而DETS把元組保存在磁盤上桑李,它的最大文件大小是2GB,DETS文件必須先打開才可以操作,用完以后還要正確關(guān)閉贵白。他們之間表的屬性也不一樣率拒,DETS在打開文件時(shí)必須賦予一個(gè)全局名稱,如果兩個(gè)或者更多的進(jìn)程以同一個(gè)名稱和選項(xiàng)打開了文件禁荒,他們就會(huì)共享這個(gè)表猬膨,直到所有進(jìn)程都關(guān)閉他,否則一直是打開狀態(tài)

%拿例子說(shuō)話
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).
    
%這個(gè)例子是dets打開和關(guān)閉的例子呛伴,我們使用了模塊名作為表名
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.
 %這個(gè)例子就包含了讀和寫

手冊(cè)內(nèi)推薦閱讀有關(guān)ETS和DETS的函數(shù)

  • 基于模式獲取和刪除對(duì)象
  • ETS和DETS勃痴、以及ETS表和文件之間的互相轉(zhuǎn)換
  • 查看表的資源占用情況
  • 遍歷表內(nèi)所有的元素
  • 修復(fù)損壞的DETS表
  • 讓表可視化

ETS表的各種方法:
ets:all/0
獲取所有的 ets 表

ets:delete/1
刪除整張表

ets:delete/2
刪除表里指定鍵的所有數(shù)據(jù)

ets:delete_all_objects/1
刪除表里的所有數(shù)據(jù)

ets:delete_object/2
刪除表里的指定數(shù)據(jù)

ets:file2tab/1
從一個(gè)文件讀取一個(gè) ETS 表

ets:first/1
獲取 ETS 表里的第一個(gè)對(duì)象數(shù)據(jù)的鍵

ets:foldl/3
對(duì) ETS 數(shù)據(jù)遍歷循環(huán)操作

ets:fun2ms/1
把語(yǔ)法函數(shù)轉(zhuǎn)為匹配規(guī)范的偽函數(shù)

ets:give_away/3
改變一個(gè)表的擁有者

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

ets:info/1
返回一個(gè) ETS 表的信息

ets:info/2
返回給出的跟表相關(guān)的項(xiàng)的信息

ets:insert/2
向 ETS 表插入數(shù)據(jù)

ets:insert_new/2
向 ETS 表插入新數(shù)據(jù)

ets:is_compiled_ms/1
檢測(cè)一個(gè) Erlang 數(shù)據(jù)是否是一個(gè)有效已編譯的匹配規(guī)范

ets:last/1
返回表里的最后一個(gè)鍵

ets:lookup/2
在 ETS 表里查出相應(yīng)鍵的值

ets:lookup_element/3
返回 ETS 表里指定鍵的對(duì)象數(shù)據(jù)的第幾個(gè)元素?cái)?shù)據(jù)

ets:match/1 | ets:match/2 | ets:match/3
根據(jù)匹配模式匹配表里的對(duì)象數(shù)據(jù)

ets:match_delete/2 | ets:match_object/1 | ets:match_object/2 | ets:match_object/3
根據(jù)匹配模式刪除表里的對(duì)象數(shù)據(jù)

ets:match_spec_compile/1
把一個(gè)匹配規(guī)范編譯為它的內(nèi)部表示形式

ets:match_spec_run/2
使用一個(gè)匹配規(guī)范來(lái)執(zhí)行匹配操作

ets:member/2
判斷表里面是否存在指定鍵的數(shù)據(jù)

ets:new/2
創(chuàng)建一個(gè) ets 表

ets:next/2
返回表的下一個(gè)鍵

ets:prev/2
返回表的上一個(gè)鍵

ets:rename/2
重新給 ETS 表命名一個(gè)名字

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

ets:select/1
對(duì) ETS 表里的數(shù)據(jù)進(jìn)行匹配比對(duì)

ets:select_delete/2
根據(jù)匹配模式刪除表里的對(duì)象數(shù)據(jù)

ets:tab2file/2 | ets:tab2file/2
把一個(gè) ETS 表轉(zhuǎn)儲(chǔ)到一個(gè)文件里

ets:tab2list/1
返回一個(gè) ETS 表的所有對(duì)象數(shù)據(jù)的列表

ets:to_dets/2
把內(nèi)存里的 ETS 數(shù)據(jù)插入到磁盤上保存

ets:update_element/3
更新 ETS 表里指定鍵的對(duì)象數(shù)據(jù)的第幾個(gè)元素?cái)?shù)據(jù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末题翻,一起剝皮案震驚了整個(gè)濱河市狭郑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耘拇,老刑警劉巖姐军,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铁材,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡奕锌,警方通過查閱死者的電腦和手機(jī)著觉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)惊暴,“玉大人饼丘,你說(shuō)我怎么就攤上這事×苫埃” “怎么了肄鸽?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)屡穗。 經(jīng)常有香客問我贴捡,道長(zhǎng),這世上最難降的妖魔是什么村砂? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任烂斋,我火速辦了婚禮,結(jié)果婚禮上础废,老公的妹妹穿的比我還像新娘汛骂。我一直安慰自己,他們只是感情好评腺,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布帘瞭。 她就那樣靜靜地躺著,像睡著了一般蒿讥。 火紅的嫁衣襯著肌膚如雪蝶念。 梳的紋絲不亂的頭發(fā)上抛腕,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音媒殉,去河邊找鬼担敌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛廷蓉,可吹牛的內(nèi)容都是我干的全封。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼桃犬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼刹悴!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起攒暇,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤土匀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后扯饶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恒削,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年尾序,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躯砰。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡每币,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出琢歇,到底是詐尸還是另有隱情兰怠,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布李茫,位于F島的核電站揭保,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏魄宏。R本人自食惡果不足惜秸侣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宠互。 院中可真熱鬧味榛,春花似錦、人聲如沸予跌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)券册。三九已至频轿,卻和暖如春垂涯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背航邢。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工耕赘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翠忠。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓鞠苟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親秽之。 傳聞我的和親對(duì)象是個(gè)殘疾皇子当娱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348