erlang初涉及7_ETSDETS用法

erlang初涉及7_ETS/DETS用法

ets、dets是两个系统模块,在erlang中可以高效存储海量数据。ETS的缩写是Erlang Term Storage(Erlang数据存储)的缩写,DETS则是Disk ETS(磁盘ETS)的缩写。今天我们来了解ETS和DETS的用法。

表的四种类型

保存的是元组,元组里的某一个元素(默认是第一个)被称为该表的键.通过键可以向表里插入和提取元组.分为四个基本的表类型

  • 异键(set):即每个元组的键值不能相同
  • 同键(bag):多个元组可以有相同的键值,但不能有两个完全相同的元组
  • 有序异键(ordered set):排好序的set表
  • 副本同键(duplicate bag):多个元组可以有相同的键值,且同一个元组可以在表中出现多次

对表的几个基本操作

  • 创建一个新表或打开现有的表: 用ets:newdets:open_file实现
  • 向表里插入一个或多个元组: insert(TableId,X),其中X是一个元组或元组列表
  • 在表里查找某个元组: lookup(TableId,Key) ,得到的结果是一个匹配key的元组列表
  • 丢弃某个表: dets:close(TableId)ets:delete(TableId).

创建一个表

ets:new(Name,[Opt]) -> TableId : Name是一个原子,[Opt]是一列选项分为:

  • set|ordered_set|bag|duplicate_bag : 创建一个指定类型的ETS表
  • private 创建一个私有表,只有主管进程才能读取和写入它
  • public 创建一个公共表,任何知道此表标识符的进程都能读取和写入它
  • protected 创建一个受保护表,任何知道此表标识符的进程都能读取它,但只有主管进程才能写入它
  • named_table 如果设置了此选项,Name就可以被用于后续的表操作
  • {keypos,K} 用K作为键的位置,通常键的位置是1。基本上唯一需要使用这个选项的场合是保存Erlang记录(它其实是元组的另一种形式),并且记录的第一个元素包含记录名的时候。

下面我们来看一个例子

est_test.erl

-module(ets_test).
-export([start/0]).
 
start()->
    lists:foreach(fun test_ets/1,[set,ordered_set,bag,duplicate_bag]).
test_ets(Mode)->
    TableId=ets:new(test,[Mode]),
    ets:insert(TableId,{a,1}),
    ets:insert(TableId,{b,2}),
    ets:insert(TableId,{a,1}),
    ets:insert(TableId,{a,3}),
    List=ets:tab2list(TableId),
    io:format("~-13w => ~p~n",[Mode,List]),
    %这里是格式化输出
    ets:delete(TableId).

输出运行

1>ets_test:start().
set           => [{b,2},{a,3}]
ordered_set   => [{a,3},{b,2}]
bag           => [{b,2},{a,1},{a,3}]
duplicate_bag => [{b,2},{a,1},{a,1},{a,3}]

这里我们就可以直观的发现几种类型的表有什么区别

DETS(简单涉及)

如果在打开DETS文件后没有关闭, 则Erlang将自动进行修复, 而修复可能会耗费大量的时间, 因此应在使用完后关闭DETS文件。
打开一个DETS表时, 要给出一个全局性的名字, 以便于多个进程用相同的名字和属性来共享这个表。

来个例子看看

lib_filenames_dets.erl

-module(lib_filenames_dets).
-export([open/1, close/0, test/0, filename2index/1, index2filename/1]).

open(File) ->
    io:format("dets opened:~p~n", [File]),
    Bool = filelib:is_file(File),
    %% 使用?MODULE宏获取文件名作为TableName
    case dets:open_file(?MODULE, [{file, File}]) of
        {ok, ?MODULE} ->
            case Bool of
                true  ->void;
                %% 插入键为free, 值为1的记录, 用于统计表中记录的总数
                false ->ok = dets:insert(?MODULE, {free, 1})
            end,
            true;
        {error, _Reason} ->
            io:format("cannot open dets table~n"),
            exit(eDetsOpen)
    end.

close() ->dets:close(?MODULE).

%% 将二进制格式的文件名插入到dets表
filename2index(FileName) when is_binary(FileName) ->
    %% 插入前先查询文件名是否已经存在
    case dets:lookup(?MODULE, FileName) of
        [] ->
            %% 不存在则先查询最大索引, 然后将索引-文件名, 文件名-索引, free-最大索引插入到dets表
            [{_, Free}] = dets:lookup(?MODULE, free),
            ok = dets:insert(?MODULE, [{Free, FileName}, {FileName, Free}, {free, Free+1}]), 
            Free;
        %% 存在则直接返回索引值
        [{_, N}] ->N
    end.

%% 根据索引值查询文件名
index2filename(Index) when is_integer(Index) ->
    case dets:lookup(?MODULE, Index) of
        []         ->error;
        [{_, Bin}] ->Bin
    end.

test() ->   
    %% 每次测试都重建dets表
    file:delete("./filenames.dets"),
    open("./filenames.dets"),
    %% 使用正则查找当前目录下的所有erl文件添加到dets表中
    F = lib_files_find:files(".", ".*[.](erl).*$", true),
    lists:foreach(fun(I) ->filename2index(list_to_binary(I)) end, F).

运行结果

1> c(lib_filenames_dets).
{ok,lib_filenames_dets}
2> lib_filenames_dets:test().
dets opened:"./filenames.dets"
ok
3> lib_filenames_dets:index2filename(1).
<<"./lib_trigrams.erl">>
4> lib_filenames_dets:index2filename(2).
<<"./lib_files_find.erl">>
5> lib_filenames_dets:index2filename(3).
<<"./lib_filenames_dets.erl">>
6> lib_filenames_dets:index2filename(4).
<<"./ets_test.erl">>
7> lib_filenames_dets:filename2index(list_to_binary("./ets_test.erl")).
上一篇:Mybatis-plus 设置 @TableId(type = IdType.AUTO) 并没有解决自增长问题


下一篇:mybatisplus自增主键实现返添加后的数据