Eralng ets学习总结

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

[b]ets有4种类型的table:[/b]
[table]
|set|table中的每一个Value(Tuple)都是唯一,并且一个Key只能对应一个Value|
|ordered_set|同set,唯一不同的是table中的Key是有序的|
|bag|table中的每一个Value都是唯一,但是一个key可以对应多个Value|
|duplicate_bag|table中每一个key可以对应多个Value,并且允许Value重复|
[/table]
[b]那么table中的Key如何定义,Value又是什么呢?[/b]
table中的Value是一个tuple,而它的Key就是tuple中的第一个元素(默认),譬如,我往ets table插入一个{name, diaocow},那么Key就是name,Value就是{name, diaocow},于是就在ets table中建立了这么一条映射关系:name -> {name, diaocow}

[b]这4种类型table有什么区别,查找效率又如何?[/b]
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重复,所以它在插入时就完全不需要考虑这些,只管插,所以效率很高.

[b]现在我们就来看看书上的一个例子,加上理解:[/b]
[img]http://dl.iteye.com/upload/attachment/0079/1838/0081ec43-cf20-3d1b-abbe-bd50146644e1.jpg[/img]

运行结果:
[img]http://dl.iteye.com/upload/attachment/0079/1840/9786d0c6-7f6e-305a-8a9e-2895cc637c3e.jpg[/img]

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

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

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

[table]
|set,ordered_set,bag,duplicate_bag|指定创建的table类型|
|public,private,protected|指定table的访问权限,若是public表示所有process都可以对该table进行读写(只要你知道TableId或者TableName),private表示只有创建表的process才能对table进行读写,而protected则表示所有的process都可以对表进行读取,但是只有创建表的process能够对表进行写操作[b](ps: ets table仅可以被同一个erlang node中的processes共享)[/b]|
|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将会收到一个消息:[b]{'ETS-TRANSFER',tid(),FromPid,HeirData}[/b],这样表的拥有权就转交了,我们可以看下面这段测试代码|
[/table]
[img]http://dl.iteye.com/upload/attachment/0079/1842/75fce172-8e50-313e-a9f4-c612ef8e7cc5.jpg[/img]
运行结果:
[img]http://dl.iteye.com/upload/attachment/0079/1844/de719aab-e9d7-3dab-a06e-09347c194d16.jpg[/img]

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

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

那么ets是如何判断两个Key是否相同呢?它分为[b]match[/b]和[b]compare equal[/b] 两种情况:
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方式匹配,我们来看下面一个例子:

[img]http://dl.iteye.com/upload/attachment/0079/2112/334bdfda-9e04-394b-b890-1fb2e18ff3a5.jpg[/img]
这是一个set类型table,所以在比较Key时采用match方式进行匹配(既比较类型也比较值),所以它认为1.0和1是两个不同的Key,因此插入了两个Tuole,现在我们再看另外一个例子,对比一下:

[img]http://dl.iteye.com/upload/attachment/0079/2114/8c784b74-4318-3f35-861f-fb1ab8eea6f8.png[/img]
对于ordered_set类型table,它采用compare equal方式进行匹配(仅比较值),所以它认为1.0和1是两个相同的Key,因此用新值{1.0,3} 替换了老值{1, 2}(注意看tab2list方法),相关官方文档:
[quote]Also worth noting is the subtle difference between [b]matching[/b] and [b]comparing equal[/b], which is demonstrated by the different table types set and ordered_set. [b]Two Erlang terms match if they are of the same type and have the same value[/b], so that 1 matches 1, but not 1.0 (as 1.0 is a float() and not an integer()). [b]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[/b], 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.[/quote]
[b]3. ets:lookup(Tab, Key) -> [Object][/b]
第一个参数Tab,不再解释,与insert函数中的一样,第二个参数就是需要查找的Key,它以list的形式返回符合条件的tuple(可以参看我们上一个例子代码),相关官方文档:
[quote]In the case of [b]set, bag and duplicate_bag[/b], an object is returned only if the given key [b]matches[/b] the key of the object in the table. If the table is an [b]ordered_set[/b] however, an object is returned if the key given [b]compares equal[/b] 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.[/quote]
除了使用lookup来查找,ets还提供了一些更为强大的查找方法:

[b]4.match(Tab, Pattern) -> [Match][/b]
该方法用来查找table中所有匹配Pattern的tuple并返回tuple中某些或全部元素(这个由pattern指定),譬如:
[img]http://dl.iteye.com/upload/attachment/0079/2118/e8a57264-495e-3a54-842c-e55350a357f4.png[/img]

运行结果:
[img]http://dl.iteye.com/upload/attachment/0079/2120/c53e1bec-661e-398e-8897-bf2a907fbf34.png[/img]

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

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

我们除了可以一次性查找出所有满足匹配的tuples,也可以采用"分页查询"的方式查询,即每次只查询出部分结果,然后通过迭代查找出所有结果:
[img]http://dl.iteye.com/upload/attachment/0079/2124/0ef135e1-1477-3ac5-a5d9-73579c2fb18b.png[/img]

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

[b]最后我们来看下ets table的遍历[/b]
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进行遍历:
[img]http://dl.iteye.com/upload/attachment/0079/2128/9fb93295-87fb-354b-8f95-7954094f634d.png[/img]

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

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的遍历

[color=red]-------------------------------------------------------------------------------华丽分割线-------------------------------------------------------------------------------[/color]

[b]ets其他一些细节:[/b]
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.如果比较的两个值不是同一种数据类型,则按照这种规则比较:[b]number < atom < reference < fun < port < pid < tuple < list < bit string[/b];
b.如果比较的两个值都是List,那么它会逐个比较其中元素;
3.如果比较的两个值都是Tuple,那么它首先会比较Tuple Size是否相同(即元素个数),若Size相同则逐个比较其中的元素;
[img]http://dl.iteye.com/upload/attachment/0079/2130/746b7aae-769e-32ac-bd5b-fc3b64d87cc7.png[/img]


关于ets更详细内容,请参看文档:[url]http://www.erlang.org/doc/man/ets.html[/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值