用ETS和DETS存储数据
ets和dets是两个系统模块,可以用来高效存储海量的Erlang数据。ETS是Erlang Term Storage(Erlang数据存储)的缩写,DETS则是Disk ETS(磁盘ETS)的缩写。
表的类型共有4种,分别是异键表(set),同键表(bag),有序异键(ordered set),副本同键(duplicate bag)。
创建与销毁ETS表
可以看到在异键表中,不允许出现相同键的kv对存在,在同键表中会以最后一个存入的kv为准,而副本同键是允许多个同k的kv对存在的(顺带一提,笔者在编写时忘记做ets:delete操作,打开的表在退出时一定记得关闭,尤其是接下来的dets表)。
需要注意的是,ETS表保存在一个单独的存储区域里,与正常的进程内存无关。可以认为ETS表归创建它的进程(主管进程)所有,当这个进程挂了或者调用ets:delete时,表就会被删除。
ets:new时也有许多参数
-spec ets:new(Name, [Opt]) -> TableId
Name -> 表名原子,[Opt]则是一系列选项
1.其中有表的类型
set | ordered_set | bag | duplicate_bag
2.表的公开/保护/私有
private:主管进程可读可写
public:公共可读可写
protected:公共可读 主管可读可写
3.表的名字是否启用
named_table:通过此属性才能通过Name操作表
4.表的K位置
{keypos, K}:指定Key在第几位(默认第一位)
创建与关闭Dets表
与ets不同的是,dets需要先使dets:open_file/2 打开文件才能使用,且同一个文件可以被多个进程同时打开,如果需要关闭表,得所有打开了这个表的进程都调用了dets:close/1 之后才会关闭。
打开文件(没有则创建)
dets:open_file(table,[{file,study}]).
插入元素
dets:insert(table,{hello,world}).
查找元素
dets:lookup(table,hello).
关闭(Close)
dets:close(table)
通过open_file打开一个名为study的file(没有时会自动创建)
可以看到已经成功创建了dets表
最重要的一点是不再使用表时一定要close掉连接,否则会抛exception shutdown。
与ets相同的时,创建时可以接受多个arg作为参数,主要参数如下表:
Args = [OpenArg]
OpenArg =
{access, access()} |
周期刷新表 值为infinity时不刷新表
{auto_save, auto_save()} |
{estimated_no_objects, integer() >= 0} |
{file, file:name()} |
此选项将表分段,用以优化表关于插入操作时间
{max_no_slots, no_slots()} |
{min_no_slots, no_slots()} |
键的位置
{keypos, keypos()} |
它先把元素存储在RAM中,当调用dets:syns(Name)函数或者关闭表时,输出这些元素到文件
{ram_file, boolean()} |
指示表非正常关闭时是否需要进行修复
{repair, boolean() | force} |
可以为set、bag或者duplicate bag
{type, type()}
其他一些有关ETS与DETS的操作
ETS和DETS表还支持许多尚未在Erlang程序设计19章里没有介绍过的操作。这些操作分为以下几类:
- 基于模式获取和删除对象;
- ETS和DETS表之间的相互转换;
- 查看表的资源占用情况;
- 修复损坏的DETS表;
- 让表可视化。
1.基于模式获取和删除对象
% 创建一个ETS表
Table = ets:new(my_table, [named_table,bag]).
% 向表中插入数据
ets:insert(Table, [{apple, 1}, {pear, 2}, {orange, 3}, {grape, 4}, {watermelon, 5}, {apple, 6}]).
% 基于模式获取对象
ets:match(Table, {apple, '$1'}).
% 基于模式删除对象
ets:delete_object(Table, {pear, '$2'}).
% 打开一个DETS表
dets:open_file(table,[{file,study}]).
% 向表中插入数据
dets:insert(table, [{apple, 1}, {pear, 2}, {orange, 3}, {grape, 4}, {watermelon, 5}]).
% 基于模式获取对象
dets:match(table, {apple, '$1'}).
% 基于模式删除对象
dets:delete(table, {pear, '$2'}).
2.ETS和DETS表之间的相互转换
% 创建一个ETS表
ets:new(table, [named_table]).
% 向ETS表中插入数据
ets:insert(table, [{apple, 1}, {pear, 2}, {orange, 3}, {grape, 4}, {watermelon, 5}]).
% 将ETS表转换为DETS表
ets:to_dets(table, DetsTab).
3.查看表的资源占用情况
% 创建一个ETS表
Table = ets:new(my_table, [named_table]).
% 获取ETS表的信息,包括资源占用情况
Info = ets:info(Table).
% 打开一个DETS表
dets:open_file(table,[{file,study}]).
% 获取DETS表的信息,包括资源占用情况
dets:info(table).
4.让表可视化
% 创建一个ETS表
Table = ets:new(my_table, [named_table]).
% 向ETS表中插入数据
ets:insert(Table, [{1, "value1"}, {2, "value2"}, {3, "value3"}, {4, "value4"}]).
% 将ETS表可视化
ets:tab2list(Table).
一些有用的API以及ETS与DETS的互转
笔者翻阅网上大部分的资料都很少有提及ETS和DETS的相关API操作以及详细介绍ETS与DETS的转化过程。这里是笔者经过学习以及翻阅英文文献后获得的一些学习成果,分享给各位正在学习Ets以及Dets的小伙伴。
ets:insert_new
与直接insert不同的是,insert_new只会作用于一下情况:原来的ets表中没有将要插入的数据的Key,比起直接insert能更加保证ets的安全性。
可以通过ets:i(TableName)来查看ets中的元素,ets:i()可以查看所有正在内存中运行的ets表。
ets:lookup_element
如果表的类型是 set 或 ordered_set,那么lookup_element函数将返回键为 Key 的对象的第 Pos 个元素。
如果表的类型是 bag 或 duplicate_bag,那么该函数将返回键为 Key 的对象的第 Pos 个元素的列表。
如果表里没有键为 Key 的对象,函数将以 ==badarg ==的原因退出。
需要注意的是如果在使用ets的相关API时操作不当比如参数不对时会导致ETS异常退出数据丢失!
如图所示我们使用lookup_element去找一个不存在的Pos位的元素,导致ets异常退出,直接导致ets表丢失。
ets:delete_all_objects
功能如名字一般,删除ets表里的所有元素,效果如图:
ets:tab2list
将ets表中的所有数据转化为列表显示:
通过底层实现代码可知,本质上是调用了ets的API -> ets:match_object/2 通过模式匹配取出所有的元素
ets:update_element
更新ets表中以Key为键的第Pos位的元素为Value,此处代表Key为3的KV对的第二位元素改为13,在图中即表现为第三行元素变为了{3,13}。
值得注意的是,第三个参数是指所找到的值的第pos位置的值更改为value 而不是传的{K,V} 所以key为2的键值对不受影响!
ets:tab2file
将ets表转换为文件形式存储
把表 Table 转储到文件 Filename。
当转储表时,表的某些信息会在转储开始时存到文件头里。这些信息包括表的类型,名字,访问权限,大小,版本和是否是一个有命名的表。这也包括一些额外加进来的扩展信息,例如在文件里的对象数量、表头和记录的 MD5 校验值。
如果表是 public 类型的表,并且记录在转储的过程中被添加或删除,那么在表头上的 size 大小值也许跟在文件里的实际记录数量不一致。在转储过程中更新 public 类型的表,并且读取的适合希望验证数据,对扩展信息的某个字段的读取验证处理至少是需要的。
ets:to_dets
将ets转储到dets中
这个操作会在插入数据之前清空DETS文件!
dets:to_ets
相对的,有ets转化dets的操作,自然也有dets转化为ets的操作
不过注意,to_ets本身会将Tab这个dets表的内容写入Table这个ets表中,我们使用模式匹配生成了一个新的NewEts,它的内容和Table的内容是一致的!