erlang之ets总结

/1.

ets是什么? 

ets是Erlang Term Storage的缩写,它是一个基于内存的KV Table,支持大数据量存储以及高效查询. 



ets有4种类型的table:  
settable中的每一个Value(Tuple)都是唯一,并且一个Key只能对应一个Value
ordered_set同set,唯一不同的是table中的Key是有序的
bagtable中的每一个Value都是唯一,但是一个key可以对应多个Value
duplicate_bagtable中每一个key可以对应多个Value,并且允许Value重复

那么table中的Key如何定义,Value又是什么呢?  
table中的Value是一个tuple,而它的Key就是tuple中的第一个元素(默认),譬如,我往ets table插入一个{name, diaocow},那么Key就是name,Value就是{name, diaocow},于是就在ets table中建立了这么一条映射关系:name -> {name, diaocow} 

这4种类型table有什么区别,查找效率又如何?  
set, bag, duplicate_bag内部是采用哈希结构实现,因此查找效率很高,O(1); 
ordered_set 内部是采用平衡树结构实现(Key之间是有序的),查找效率相比其他类型略低,是logN级别的; 

至于选用何种类型的table,需要根据实际需求来确定,如果你需要元素之间有序,那么你只能选用ordered_sets(需要牺牲一点效率) 

bag,duplicate_bag 虽然在查找效率上差不多,但是在插入元素时却有可能千差万别,为什么呢? 
因为bag虽然允许table中每一个Key对应多个Value,但是它不允许这些Value重复,所以在插入具有相同Key的Value时,它必须与每一个现有元素作对比,而duplicate bag 允许table中的Value重复,所以它在插入时就完全不需要考虑这些,只管插,所以效率很高. 

现在我们就来看看书上的一个例子,加上理解:  


运行结果: 
 

注意对于set和ordered_set 当插入具有相同Key的Value时,后插入的Value会替代上一个Value(譬如这里的{a,1}被{a,3}替代),另外在erlang的每一个node中,可以创建的ets table数量是有限的(默认为1440, 不过我的测试结果确是2037??? ),这个值可以通过修改环境变量ERL_MAX_ETS_TABLES来修改(官方文档建议实际中采用的值比这个高些) 

-------------------------------------------------------------------------------华丽分割线-------------------------------------------------------------------------------  
现在我们重新回到刚才那个例子,来学习几个api: 

1.ets:new(Name, Options) -> tid() | atom()  
这个函数用来创建一个名为Name的table(若创建成功,返回一个TableId或者TableName供process引用);Options是一个选项list,用来指定创建table的各种属性,它的值有: 

set,ordered_set,bag,duplicate_bag指定创建的table类型
public,private,protected指定table的访问权限,若是public表示所有process都可以对该table进行读写(只要你知道TableId或者TableName),private表示只有创建表的process才能对table进行读写,而protected则表示所有的process都可以对表进行读取,但是只有创建表的process能够对表进行写操作(ps: ets table仅可以被同一个erlang node中的processes共享)
named_table若指定了named_table这个属性,就可以使用表名(也就是new函数的第一个参数Name)对表进行操作,而无需使用TableId
{keypos,Pos}上面说到,我们默认使用tuple中第一个元素作为Key,那么是否可以修改这个规则呢?自然可以,使用{keypos,Pos}即可,其中Pos就是表示使用tuple中第几个元素作为Key
{heir, Pid, HeirData},{heir,none}这个heir属性指明当创建table的process终止时,是否有其他process来继承这个table,默认值是{heir,none},表示没有继承者,所以当创建表的process终止时,表也随之被delete;若我们指定了{heir,Pid,HeirData},那么当创建表的process终止时,process identifer为Pid的process将会收到一个消息:{'ETS-TRANSFER',tid(),FromPid,HeirData},这样表的拥有权就转交了,我们可以看下面这段测试代码


运行结果: 
 

如果你不指定任何Options(即参数值为[]),那么ets将会使用[set, protected, {keypos,1}, {heir,none}, {write_concurrency,false}, {read_concurrency,false}]作为默认值 (关于write_concurrency和read_concurrency这个两个参数的具体含义自己不是特别的清楚,目前只是了解到该参数用于设置是否允许并发读或者写,和性能调优相关,所以暂时hold)  

2. ets:insert(Tab, ObjectOrObjects) -> true 
很显然这个函数是用来往table里插入数据,其中第一个参数Tab表示需要操作哪一张表,它可以是TableId或者TableName(若创建表时指定了named_table选项),第二个参数ObjectOrObjects表示需要插入的数据,它可以是一个tuple或者是一个tuple list;注意这个insert方法是一个原子操作,并且对于set或者ordered_set类型table,在插入具有相同Key的Value时,会用新值去替换老值; 

那么ets是如何判断两个Key是否相同呢?它分为 match compare equal  两种情况: 
1.如果两个Key(erlang term)match,那么这两个Key不仅值相同,类型也相同; 
2.如果两个Key (erlang term)compare equal, 那么只需要这两个Key的值相同,而无需类型相同(譬如 1.0 compare equal 1,但是1 not match 1.0) 

所以若两个Key match,则它们一定compare equal,反过来则不行,那在ets中到底是采用match还是compare equal进行判断与table的类型有关,其中ordered_set采用compare equal方式匹配,其余类型都采用match方式匹配,我们来看下面一个例子: 

 
这是一个set类型table,所以在比较Key时采用match方式进行匹配(既比较类型也比较值),所以它认为1.0和1是两个不同的Key,因此插入了两个Tuole,现在我们再看另外一个例子,对比一下: 

 
对于ordered_set类型table,它采用compare equal方式进行匹配(仅比较值),所以它认为1.0和1是两个相同的Key,因此用新值{1.0,3} 替换了老值{1, 2}(注意看tab2list方法),相关官方文档: 
引用
Also worth noting is the subtle difference between  matching and  comparing equal, which is demonstrated by the different table types set and ordered_set.  Two Erlang terms match if they are of the same type and have the same value, so that 1 matches 1, but not 1.0 (as 1.0 is a float() and not an integer()).  Two Erlang terms compare equal if they either are of the same type and value, or if both are numeric types and extend to the same value, so that 1 compares equal to both 1 and 1.0. The ordered_set works on the Erlang term order and there is no defined order between an integer() and a float() that extends to the same value, hence the key 1 and the key 1.0 are regarded as equal in an ordered_set table.

3. ets:lookup(Tab, Key) -> [Object]  
第一个参数Tab,不再解释,与insert函数中的一样,第二个参数就是需要查找的Key,它以list的形式返回符合条件的tuple(可以参看我们上一个例子代码),相关官方文档: 
引用
In the case of  set, bag and duplicate_bag, an object is returned only if the given key  matches the key of the object in the table. If the table is an  ordered_set however, an object is returned if the key given compares equal to the key of an object in the table. The difference being the same as between =:= and ==. As an example, one might insert an object with the integer() 1 as a key in an ordered_set and get the object returned as a result of doing a lookup/2 with the float() 1.0 as the key to search for. 

If the table is of type set or ordered_set, the function returns either the empty list or a list with one element, as there cannot be more than one object with the same key. If the table is of type bag or duplicate_bag, the function returns a list of arbitrary length.

除了使用lookup来查找,ets还提供了一些更为强大的查找方法: 

4.match(Tab, Pattern) -> [Match]  
该方法用来查找table中所有匹配Pattern的tuple并返回tuple中某些或全部元素(这个由pattern指定),譬如: 
 

运行结果: 
 

其中Pattern可以包含以下字符 
1.一个绑定变量或者任意Erlang Term; 
2.一个占位符'_',可以匹配任何Erlang Term; 
3.一个变量符'$N' (N可以为0,1,2,3....),该变量指定了match方法需要返回tuple中哪些元素; 

若Pattern中包含Key值,那么查找起来非常快速(相当于索引查询),否则需要全表遍历 

我们除了可以一次性查找出所有满足匹配的tuples,也可以采用"分页查询"的方式查询,即每次只查询出部分结果,然后通过迭代查找出所有结果: 
 

其中: 
5.match(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'   
用来返回table中满足匹配的tuples并且数量限制为Limit,返回值Continuation用来传递给 match(Continuation) 方法进行下一次迭代,直到遍历完table(返回结尾标示:'$end_of_table'); Api文档中提到:用这种方式遍历表的效率高于first和next方法(这两个方法后面会提到) 

最后我们来看下ets table的遍历  
first(Tab) -> Key | '$end_of_table' 
next(Tab, Key1) -> Key2 | '$end_of_table' 
first方法用来返回table中第一个Key,而next方法返回table中在Key1后面的Key2,如果这是一张空表或者Key1已经是表的最后一个Key,那么first方法以及next方法将返回'$end_of_table'作为结尾标记,我们可以使用这组方法对一个table的Key进行遍历: 
 

另外对于ordered_set类型table,它的Key本身就是有序的(因为它内部采用平衡树结构,按照Erlang Term Order排列),所以在遍历ordered_set类型table时,它的Key会按Erlang Term Order输出,而对于其他类型的table,遍历table时Key的顺序是不确定的;  

last(Tab) -> Key | '$end_of_table' 
prev(Tab, Key1) -> Key2 | '$end_of_table' 
这组遍历方式和first/last基本相同,唯一不同的是,它是从后往前遍历 

至此我想大家已经基本了解了: 
1.ets table的类型分类 
2.ets table的创建 
3.ets table的插入和查找 
4.ets table的遍历 

-------------------------------------------------------------------------------华丽分割线-------------------------------------------------------------------------------  

ets其他一些细节:  
1.官方文档中提到: In the current implementation, every object insert and look-up operation results in a copy of the object,这是什么意思呢? 
意思是,在当前erlang的实现版本中,插入一个tuple:会从当前process中的stack和heap拷贝tuple的一副副本到ets table中去,同样查询操作也会导致从表中拷贝一份tuple(或多个tuples)到执行查询的process的stack和heap中去,这种行为适用于erlang所有的数据类型,除了binaries; 

2.ets table是不会被垃圾回收的,只有当下面两种情况它会被销毁: 
a.调用delete方法; 
b.创建ets table的process结束; 

3.关于ordered_set类型table,它的内部元素根据Key值按照Erlang Term Order排序,那么Erlang Term Order是什么呢? 
a.如果比较的两个值不是同一种数据类型,则按照这种规则比较: number < atom < reference < fun < port < pid < tuple < list < bit string ; 
b.如果比较的两个值都是List,那么它会逐个比较其中元素; 
3.如果比较的两个值都是Tuple,那么它首先会比较Tuple Size是否相同(即元素个数),若Size相同则逐个比较其中的元素; 


 

/2.

ETS表的基本:

  1. ETS 表可以用来高效存储海量的erlang数据,ETS提供大型的键-值查询表,
  2. ETS是相当高效的:可以用它存储海量的数据(只要有足够的内存).最常用的表操作是插入和查找。
  3. ETS表的数据保存在内存里,他们是易失的。当ETS表被丢弃或控制它的进程终止时,数据就会被删除.
  4. ETS表是底层运行时系统里实现的,ETS表没有垃圾回收机制,即使ETS表及其巨大也不会有垃圾回收的负担,不过,创建和访问ETS对象任然会带来少许开销.

ETS表的实例操作: 
1. 创建一个ETS表: new/2->atom 
new(name,option) 参数介绍: 
name:ETS表名 
option:一列选项 
1) set | ordered_set | bag | duplicate_bag 创建一个指定类型的表 
2) private 创建一个私有表,只有主管进程才能读取和写入它 
3)public 创建一个公共表 , 任何进程在知道此表的标示符时都可以读取和写入 
4) protected 创建一个受保护的表, 任何进程在知道此表的标示符时都可以读取,但只有主管进程和已有写入权限 
5)named_table 如果设置了此选项,Name就可以被用于后续的表操作 
6){keypos,K} 用K作为键的位置,唯一需要使用这个选项是保存erlang记录,并且记录的第一个元素包含记录名的时候
 
-name是用于启动一个节点 
2. 插入一个数据: insert/2 ->true 
这里写图片描述 
3.查询插入数据:tab2list/1 ->[Object] 
这里写图片描述 
4.返回节点所有的表以及删除一个表 
这里写图片描述 
B进程读取A进程ETS表的实例操作: 
这里写图片描述

ETS表的各种方法: 
ets:all/0

获取所有的 ets 表

ets:delete/1

删除整张表

ets:delete/2

删除表里指定键的所有数据

ets:delete_all_objects/1

删除表里的所有数据

ets:delete_object/2

删除表里的指定数据

ets:file2tab/1

从一个文件读取一个 ETS 表

ets:first/1

获取 ETS 表里的第一个对象数据的键

ets:foldl/3

对 ETS 数据遍历循环操作

ets:fun2ms/1

把语法函数转为匹配规范的伪函数

ets:give_away/3

改变一个表的拥有者

ets:i/0

在输出端上打印显示所有 ETS 表的信息

ets:info/1

返回一个 ETS 表的信息

ets:info/2

返回给出的跟表相关的项的信息

ets:insert/2

向 ETS 表插入数据

ets:insert_new/2

向 ETS 表插入新数据

ets:is_compiled_ms/1

检测一个 Erlang 数据是否是一个有效已编译的匹配规范

ets:last/1

返回表里的最后一个键

ets:lookup/2

在 ETS 表里查出相应键的值

ets:lookup_element/3

返回 ETS 表里指定键的对象数据的第几个元素数据

ets:match/1 | ets:match/2 | ets:match/3

根据匹配模式匹配表里的对象数据

ets:match_delete/2 | ets:match_object/1 | ets:match_object/2 | ets:match_object/3

根据匹配模式删除表里的对象数据

ets:match_spec_compile/1

把一个匹配规范编译为它的内部表示形式

ets:match_spec_run/2

使用一个匹配规范来执行匹配操作

ets:member/2

判断表里面是否存在指定键的数据

ets:new/2

创建一个 ets 表

ets:next/2

返回表的下一个键

ets:prev/2

返回表的上一个键

ets:rename/2

重新给 ETS 表命名一个名字

ets:safe_fixtable/2

锁定一定 ETS 表使其可以安全遍历

ets:select/1

对 ETS 表里的数据进行匹配比对

ets:select_delete/2

根据匹配模式删除表里的对象数据

ets:tab2file/2 | ets:tab2file/2

把一个 ETS 表转储到一个文件里

ets:tab2list/1

返回一个 ETS 表的所有对象数据的列表

ets:to_dets/2

把内存里的 ETS 数据插入到磁盘上保存

ets:update_element/3

更新 ETS 表里指定键的对象数据的第几个元素数据



关于ets更详细内容,请参看文档:http://www.erlang.org/doc/man/ets.html 

原文出处:http://diaocow.iteye.com/blog/1768647

感谢作者的分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值