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:newdets: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}]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值