Erlang——ETS和DETS
ETS和DETS可以提供大型的键-值搜索表。ETS驻留在内存,DETS驻留在磁盘。ETS非常高效,无论存储多少数据,查询速度都与之无关,前提是需要拥有足够大的内存;DETS接口和ETS几乎一样,不同的是,它将数据存在磁盘上,比ets慢很多,但它更节省内存。ETS和DETS可以被多个进程共享,即可以通过这两个模块实现进程间高效的数据交换。ETS或DETS表实际上就是一系列Erlang元组。
ETS中的数据存储是临时的,也就意味着当ETS表被释放时,相应数据也会被全部丢弃。DETS的数据存储是持久的,他们写在磁盘上,就算整个系统崩溃,他们也会毫发无损。再次打开DETS表时,首先检查数据一致性,如果发现数据损坏,会试图进行修复,修复过程会遍历整个表检查,所有数据非常耗时,这个过程一般可以恢复表中所有数据,但如果最后一笔数据恰好在系统崩溃时创建,他可能会丢失。
ETS支持需要高效操纵海量数据的场合。
Erlang语言有非破坏性赋值的特性,当非破坏性赋值和纯Erlang数据结构变得难以进行。ETS是一种不错的替代方案。
ETS表看起来像是用Erlang本身实现的,其实不是,实际上是由底层运行时系统实现的,相比一般Erlang对象,他的行为很不相同。ETS表不会被垃圾回收,在使用极大的ETS表时无需顾虑垃圾回收造成的影响,但相比一般Erlang对象,ETS对象的创建和访问性能会有一些轻微的降低。
表的类型
ETS和DETSs表存储元组。操作表是基于键来进行操作的,向表中插入元组时,其行为是由表的类型和这个键的值决定的。类型为set的表,要求所有元组的键值各不相同,而类型为bags的表则允许多个元组有相同的键值。set表和bags表各自有两个变种。
异键表(set):要求表里所有的键都是唯一的;
同键表(bag):允许多个元素拥有相同的键值,但不能有两个完全相同的元组;
有序异键表(ordered set):在异键表基础上元组会被排序;
副本同键表(duplicate bag):多个元组可以拥有相同的键,而且在同一张表里可以存在多个相同的元组。
表的操作
代码演示:
-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表
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).
执行结果:
2> 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}]
ok
set:异键表,每个键都只能出现一次,先插入元组{a,1}再插入{a,3},最终值将是{a,3};
ordered_set:有序异键表,对元组做了排序;
bag:表可以有多个相同的键,元组不可重复;
duplicate_bag:允许有多个完全相同的元组,四个元组都成功插入。
常用操作总结:
1)ets:new或dets:open_file,创建一个新表,表已存在则打开现有的表。
2)insert(TableId, X),向表里插入一个或多个元组,其中X是一个元组或元组列表。
3)lookup(TableID, Key),结果是一个匹配Key的元组列表。
4)dets:close(TableId) 或 ets:delete (TableId),释放某个表。
创建ETS表
调用ets:new可以创建ets表,创建表的进程就是这个表的所有者。表创建之后,它的一系列属性设置不能再更改。如果所有者进程死掉或者调用ets:delete函数,就会自动释放表内的内存空间。
ets:new(Name,[Otp])->TableId
Name是一个原子,[Otp]是一个选项列表,取值范围如下:
1)set|ordered_set|bag|duplicate_bag。以我们上述的四种特定类型创建一个表。
2)private。创建私有表,只有所有者进程可以读写这个表。
3)public。创建公开表。所有知道这个表标识的进程都可以对这个表进行读写操作
4)protected。创建受保护的表,所有知道这个表标识的进程都可以对这个表进行读写操作,但只有这个表的所有者进程可以对这个表进行写操作。
5)name_table。命名表,如果存在这个选项,则可以在后续操作中使用name来操作表。
6){keypos,K}。用K作为键的位置,通常情况下使用的是第一个位置,可能只有一种情况才需要用到这个选项,就是当我们需要存储Erlang的记录时,记录实际上是变相的元组,它的第一个元素包含的是这个记录的名字,对每一个记录来说他的值都是一样的。
打开一个ETS表时,不带任何选项,等同于使用了这样的默认选项[set,protected,{keypos,1)]。
执行演示:
1> ets:new(table1, []).
#Ref<0.3232365686.4277796866.152515>
2> ets:new(table2, [named_table]).
table2
查看表的信息
执行演示:
1> ets:new(test, [named_table]).
test
2> ets:insert(test, [{a,1},{b,2},{c,3},{a,4}]).
true
3> ets:i(test).
<1 > {a,4}
<2 > {b,2}
<3 > {c,3}
EOT (q)uit (p)Digits (k)ill /Regexp -->q
ok
6> ets:i().
id name type size mem owner
----------------------------------------------------------------------------
ac_tab ac_tab set 6 945 application_controller
file_io_servers file_io_servers set 0 305 file_server_2
global_locks global_locks set 0 305 global_name_server
global_names global_names set 0 305 global_name_server
global_names_ext global_names_ext set 0 305 global_name_server
global_pid_ids global_pid_ids bag 0 305 global_name_server
global_pid_names global_pid_names bag 0 305 global_name_server
inet_cache inet_cache bag 0 305 inet_db
inet_db inet_db set 23 500 inet_db
inet_hosts_byaddr inet_hosts_byaddr bag 0 305 inet_db
inet_hosts_byname inet_hosts_byname bag 0 305 inet_db
inet_hosts_file_byaddr inet_hosts_file_byaddr bag 0 305 inet_db
inet_hosts_file_byname inet_hosts_file_byname bag 0 305 inet_db
logger logger set 6 1531 logger
#Ref<0.3790293721.2668756996.226479> code set 353 15130 code_server
#Ref<0.3790293721.2668756996.226480> code_names set 39 4674 code_server
#Ref<0.3790293721.2668756996.226551> logger_proxy set 1 312 logger_proxy
#Ref<0.3790293721.2668756996.226565> logger_std_h_default set 1 312 logger_std_h_default
#Ref<0.3790293721.2668756996.226589> shell_records ordered_set 0 139 <0.64.0>
ok
7> ets:all().
[logger,ac_tab,#Ref<0.2125831126.1328939011.231649>,
#Ref<0.2125831126.1328939011.231650>,inet_db,inet_cache,
inet_hosts_byname,inet_hosts_byaddr,inet_hosts_file_byname,
inet_hosts_file_byaddr,global_locks,global_names,
global_names_ext,global_pid_names,global_pid_ids,
file_io_servers,#Ref<0.2125831126.1328939011.231721>,
#Ref<0.2125831126.1328939011.231735>,
#Ref<0.2125831126.1328939011.231759>,test]
8> ets:info(test).
[{id,#Ref<0.2125831126.1328939011.231796>},
{read_concurrency,false},
{write_concurrency,false},
{compressed,false},
{memory,326},
{owner,<0.77.0>},
{heir,none},
{name,test},
{size,3},
{node,nonode@nohost},
{named_table,true},
{type,set},
{keypos,1},
{protection,protected}]
9> ets:info(test,size).
3
10> ets:tab2list(test).
[{c,3},{b,2},{a,4}]
11> ets:whereis(test).
#Ref<0.2125831126.1328939011.231796>
1)ets:insert函数,向表中插入数据,Key重复的话会做数据覆盖;
2)ets:i(Table),显示表内数据;
3)ets:i(),显示所有表的信息;
4)ets:all(),本节点上所有表的列表,已经命名的表显示表名,未命名的表显示表的标志符,无法保证返回列表的一致性;
5)ets:info(Table),以元组列表形式返回关于表的信息;
6)ets:info(Table, Item),返回与表的信息中指定Key为Item的信息;
7)ets:tab2list(Table) ,返回表中所有对象的列表;
8)ets:whereis(TableName),返回由指定表的 tid(),如果不存在该表,则返回undefined。
对于表的操作
1、删除表
delete(Table)
删除表结构
执行演示:
8> ets:delete(test).
true
10> ets:info(test).
undefined
2、修改表名字
rename(Table, Name)
将表名修改为Name
执行演示:
12> ets:new(test, [named_table]).
test
13> ets:rename(test,x).
x
14> ets:info(test).
undefined
15> ets:info(x).
[{id,#Ref<0.2125831126.1328939011.231862>},
{read_concurrency,false},
{write_concurrency,false},
{compressed,false},
{memory,305},
{owner,<0.90.0>},
{heir,none},
{name,x},
{size,0},
{node,nonode@nohost},
{named_table,true},
{type,set},
{keypos,1},
{protection,protected}]
3、新增数据
1)insert(Table, ObjectOrObjects)
将对象或对象列表插入表中
2)insert(Table, ObjectOrObjects)
同样可以插入数据,不同的是键值相同不会做数据覆盖(对于 set 或 ordered_set),也不会添加更多具有表中已有键的数据(对于 bag 和 duplicate_bag)
执行演示:
1> ets:new(set,[set,named_table]).
set
2> ets:insert(set,[{b,2},{c,3}]).
true
3> ets:tab2list(set).
[{c,3},{b,2}]
4> ets:insert(set,{b,100}).
true
5> ets:tab2list(set).
[{c,3},{b,100}]
6> ets:insert_new(set,{b,0}).
false
7> ets:insert_new(set,{e,5}).
true
8> ets:new(bag,[bag,named_table]).
bag
9> ets:insert(bag,[{a,1},{b,2},{c,3}]).
true
10> ets:tab2list(bag).
[{c,3},{b,2},{a,1}]
11> ets:insert(bag,[{a,1},{a,10},{d,4}]).
true
12> ets:tab2list(bag).
[{d,4},{c,3},{b,2},{a,1},{a,10}]
可见对于set 调用insert_new函数,当键重复则返回false。
3、删除数据
1)delete(Table, Key)
删除表中键为 Key 的所有数据,若无这种数据,函数不会执行。
2)delete_object(Table, Object)
删除表中元组为Object 的所有数据。
执行演示:
28> ets:tab2list(bag).
[{d,4},{c,3},{b,2},{a,1},{a,10}]
29> ets:delete(bag,a).
true
30> ets:tab2list(bag).
[{d,4},{c,3},{b,2}]
31> ets:delete_object(bag,{c,3}).
true
match相关函数(对 ETS 表里的数据进行匹配比对)
match(Table, Pattern)
将表 Table表 中的对象与 Pattern 进行模式匹配。返回能成功匹配到Pattern的数据的列表。
Pattern可以是:一个原子(表示这个位置必须是这个值);‘_‘占位符(表示可以为任意值);’$N’(N=0,1,… 模式变量,表示匹配得到的值)。
用指定的key匹配效率会更高。
match(Table, Pattern, Limit)
与match(Table, Pattern)效果相似,但只返回能成功匹配的有限数量的对象,即Limit个对象。在随后调用 match(Con)时,可以使用 Con来获取下一个匹配对象块。
match_delete(Table, Pattern)
将Table表中能与Pattern成功匹配的对象数据都删除掉。
match_object(Table, Pattern)
match_object(Table, Pattern, Limit)
match_object(Continuation)
match_object函数和match相似,返回的是整个对象。用’$N’和用’_'效果相同。
执行演示:
1> ets:new(bag,[bag,named_table]).
bag
2> ets:insert(bag,[{dog,black,9},{fox,white,9},{bear,white,1},{dog,brown,1},{dog,white,1},{dog,black,2}]).
true
3> ets:tab2list(bag).
[{fox,white,9},
{dog,black,9},
{dog,brown,1},
{dog,white,1},
{dog,black,2},
{bear,white,1}]
4> ets:match(bag,{'_',white,'$1'}).
[[9],[1],[1]]
5> ets:match(bag,{wolf,'_','$1'}).
[]
6> ets:match(bag,{dog,'$2','$1'}).
[[9,black],[1,brown],[1,white],[2,black]]
7> {Match,Con} = ets:match(bag,{dog,'$1','_'},2).
{[[white],[black]],
{#Ref<0.607081514.1965686788.15052>,-1,2,
#Ref<0.607081514.1965686788.15085>,
[[black],[brown]],
2}}
8> ets:match(Con).
{[[black],[brown]],'$end_of_table'}
9> ets:match_delete(bag,{'_','_',9}).
true
10> ets:tab2list(bag).
[{dog,brown,1},{dog,white,1},{dog,black,2},{bear,white,1}]
11> ets:match_object(bag,{'_',white,'$1'}).
[{dog,white,1},{bear,white,1}]
12> ets:match_object(bag,{'_',white,'_'}).
[{dog,white,1},{bear,white,1}]
select相关函数(对 ETS 表里的数据进行匹配比对)
select(Table, MatchSpec)
select函数类似match。
select_count(Table, MatchSpec)
统计表中匹配对象的个数。
select_delete(Table, MatchSpec)
删除表中能成功匹配的对象,并返回删除的对象数目
1> ets:new(bag,[bag,named_table]).
bag
2> ets:insert(bag,[{dog,black,9},{fox,white,9},{bear,white,1},{dog,brown,1},{dog,white,1},{dog,black,2}]).
true
3> ets:tab2list(bag).
[{fox,white,9},
{dog,black,9},
{dog,brown,1},
{dog,white,1},
{dog,black,2},
{bear,white,1}]
4> ets:select(bag,[{{dog,'$1','$2'},[{'<','$2',3}],['$_']}]).
[{dog,brown,1},{dog,white,1},{dog,black,2}]
5> ets:select(bag,[{{dog,'$1','$2'},[{'<','$2',3}],['$$']}]).
[[brown,1],[white,1],[black,2]]
6> ets:select(bag,[{{'$0','$1','$2'},[{'>','$2',3}],[['$$','$_',it,is,a,'$1','$0']]}]).
[[[fox,white,9],{fox,white,9},it,is,a,white,fox],
[[dog,black,9],{dog,black,9},it,is,a,black,dog]]
7> ets:select_count(bag,[{{dog,'$1','$2'},[{'<','$2',3}],['$_']}]).
0
8> ets:select_count(bag,[{{dog,'$1','$2'},[{'<','$2',3}],[true]}]).
3
9> ets:select_delete(bag,[{{dog,'$1','$2'},[{'<','$2',3}],[true]}]).
3
10> ets:tab2list(bag).
[{fox,white,9},{dog,black,9},{bear,white,1}]
select_count, select_delete函数传参的第三个元素应为 true才能匹配成功。
PS D:\Erlang6m\first\src> erl
stopped
3> test_mnesia:start().
ok
4> test_mnesia:reset_tables().
{atomic,ok}
5> test_mnesia:demo(select_shop).
[{shop,potato,2456,1.2},
{shop,apple,20,2.3},
{shop,orange,100,3.8},
{shop,pear,200,3.6},
{shop,banana,420,4.5}]
6> test_mnesia:demo(select_some).
[{potato,2456},
{apple,20},
{orange,100},
{pear,200},
{banana,420}]
7> test_mnesia:demo(reorder).
[apple,orange,pear]
8> test_mnesia:demo(join).
[apple]
9> test_mnesia:demo(select_shop).
[{shop,potato,2456,1.2},
{shop,apple,20,2.3},
{shop,orange,100,3.8},
{shop,pear,200,3.6},
{shop,banana,420,4.5}]
10> test_mnesia:add_shop_item(peach,100,3.0).
{atomic,ok}
11> test_mnesia:add_shop_item(orange,236,2.8).
{atomic,ok}
12> test_mnesia:demo(select_shop).
[{shop,potato,2456,1.2},
{shop,apple,20,2.3},
{shop,peach,100,3.0},
{shop,orange,236,2.8},
{shop,pear,200,3.6},
{shop,banana,420,4.5}]
13> test_mnesia:remove_shop_item(lemon).
{atomic,ok}
14> test_mnesia:demo(select_shop).
[{shop,potato,2456,1.2},
{shop,apple,20,2.3},
{shop,peach,100,3.0},
{shop,orange,236,2.8},
{shop,pear,200,3.6},
{shop,banana,420,4.5}]