http://diaocow.iteye.com/blog/1768647

http://www.cnblogs.com/me-sa/archive/2011/08/11/erlang0007.html

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

ets有4种类型的table:

set table中的每一个Value(Tuple)都是唯一,并且一个Key只能对应一个Value
ordered_set 同set,唯一不同的是table中的Key是有序的
bag table中的每一个Value都是唯一,但是一个key可以对应多个Value
duplicate_bag table中每一个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是否相同呢?它分为matchcompare 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 givencompares 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相同则逐个比较其中的元素; 

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

ets学习的更多相关文章

  1. erl0002-erlang ets学习笔记

    ets全称“erlang term storage” erlang项式存储. ets打破了erlang“不变数据”的原则,使得进程之间可以共享数据.首先引起的思考是为什么会出现ets?下面是对网络资料 ...

  2. linux内核数据结构学习总结

    目录 . 进程相关数据结构 ) struct task_struct ) struct cred ) struct pid_link ) struct pid ) struct signal_stru ...

  3. 一、Python学习之路

    基础篇 第一章         Python介绍.安装.使用 Python 简介 Python 安装 第一个Python程序 Python 解释器 字符编码与解码 动态语言与静态语言的区别 变量及简单 ...

  4. (转)Maven学习总结(三)——使用Maven构建项目

    孤傲苍狼 只为成功找方法,不为失败找借口! Maven学习总结(三)——使用Maven构建项目 maven作为一个高度自动化构建工具,本身提供了构建项目的功能,下面就来体验一下使用maven构建项目的 ...

  5. AngularJs HTML DOM、AngularJS 事件以及模块的学习(5)

    今天的基础就到了操作DOM,事件和模块的学习,其实我个人感觉学习起来AngularJS并没有想象中的那么的艰难,可能是因为这个太基础化吧,但是我们从初学开始就应该更加的自信一些,后来我可能会写一个小的 ...

  6. 【RabbitMQ学习记录】- 消息队列存储机制源码分析

    本文来自 网易云社区 . RabbitMQ在金融系统,OpenStack内部组件通信和通信领域应用广泛,它部署简单,管理界面内容丰富使用十分方便.笔者最近在研究RabbitMQ部署运维和代码架构,本篇 ...

  7. ejabberd学习1

    ejabberd是XMPP协议的一个实现,对大家的另一个意义就是,可以通过ejabberd来学习erlang. 1.从源码安装ejabberd. 可以参考ejabberd 简明配置.这个博文包括了安装 ...

  8. Erlang 学习笔记

    http://wenku.baidu.com/link?url=AUQR8Hn-e-fEB_lqjXsd8XfapWj1qAK7J05JoBXFib_LlSk5qSOTia8HIxNV1XkeZi-k ...

  9. Sping学习笔记(一)----Spring源码阅读环境的搭建

    idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...

随机推荐

  1. Redis的高级应用-事务处理、持久化、发布与订阅消息、虚拟内存使用

    三.事务处理 Redis的事务处理比较简单.只能保证client发起的事务中的命令可以连续的执行,而且不会插入其他的client命令,当一个client在连接 中发出multi命令时,这个连接就进入一 ...

  2. 16.用Spring Boot颠覆Java应用开发

    转自:https://www.cnblogs.com/aishangJava/p/5971288.html Java开发概述: 使用Java做Web应用开发已经有近20年的历史了,从最初的Servle ...

  3. stm32单片机的封装

    接着去查看VREF...

  4. mysql 配置,还得多看看~

    http://blog.chinaunix.net/uid-20639775-id-154429.html

  5. ThreadLocal深入理解与内存泄露分析

    ThreadLocal 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本.所以每个线程都能够独立地改变自己的副本.而不会影响其他线程所相应的副本. ...

  6. OAuth2 社区通用组件

    转载:http://www.cyqdata.com/download/article-detail-54302   使用本组件,只需要几行代码,就可以在网站上集成以下效果:     相关文章及使用说明 ...

  7. 【BZOJ 2754】[SCOI2012]喵星球上的点名

    [链接]h在这里写链接 [题意]     n个人;     由姓和名组成.s1[i]和s2[i];     有m个询问串.     问你第j个询问串,是否为某个人的姓或者名的子串.     如果是的话 ...

  8. @EnableAsync和@Async开始异步任务支持

    Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程.使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor.在开发中实现异步任务,我们 ...

  9. css3-12 transform:scale(1.2,1.2)实现移入元素变大特效

    css3-12 transform:scale(1.2,1.2)实现移入元素变大特效 一.总结 一句话总结:transform:scale(1.2,1.2)鼠标移入的时候变大一点点,超出边框的部分隐藏 ...

  10. embed-it_Integrator memory compile工具使用之一

    embed-it_Integrator memory compile工具使用之一 主要内容 使用Integrator compile memory 使用Integrator 对比筛选适合的memory ...