ets
模块
ets
模块摘要
内置的数据条目存贮
描述
该模块是erlang内置数据条目存贮BIF的接口.它具备在erlang运行系统中存贮大量数据的能力,并且访问数据的时间是常量级的。(在ordered_set中,访问数据的时间与数据量的对数成正比).数据被组织成可以存贮元组的动态表。每个表在进程中创建,当进程退出时,表自动销毁。表在创建时可以设置访问权限。
表的类型有四种:set,ordered_set,bag和duplicate_bag. set和ordered_set类型的表一个键只能关联一个对象,bag和duplicate_bag类型的表一个键可以关联多个对象。一个erlang节点中的ets表的数量是有限的。目前默认的限制数量大约是1400个.这个数量上限可以在启动erlang系统前,通过设置环境变量ERL_MAX_ETS_TABLES来改变。实际的限制数会比指定的要高一些,但不会低于指定数量。
ets表不会被垃圾回收,除非它的所属进程退出,否则即使没有任何进程引用它,它也不会自动销毁。可以通过主动的调用delete/1来销毁一张表。默认的所属进程就是创建该表的进程。所属关系可在进程退出时通过heir操作来传递,或者主动调用give_away/3来完成。
一些实现细节:
.目前的实现中,每次插入或查询操作都会对对象进行一次拷贝;
.'$end_of_table'不能用作键,因为这个原子在使用first/next时被用作表尾的标记。
另外需要注意的是,在set和ordered_set表匹配和相等的微小差异。当两个erlang数据项类型和值都相同时它们才匹配,所以1和1匹配,但和1.0不能匹配(因为1.0是浮点数,而1是整数).而当两个erlang数据项类型和值都相同或者两者都是数字,并且值相同时,两者就相等,所以1和1,1.0相等。ordered_set使用erlang数据项顺序并且没有界定值相同的整型和浮点数之间的顺序,因些,键1和1.0在ordered_set表中被认为是相同的。
失败的情况
通常情况下,如果参数的格式不对,或者表的标识符无效,或者操作因访问权限被拒绝,下面的函数将以badarg的原因而退出。
并发
该模块对并发访问作了一些限制.所有对单个对象的更新操作都保证是原子的和隔离的.这意味着对单个对象的更新操作要么成功,要么失败而没有任何影响.更新操作的任何中间结果对其他进程都是不可见的.一些更新多个对象的的函数声称其整个操作都保证了原子性和隔离性.在数据库数据项中,隔离等级可以看成是"可持续的",只要所有的隔离操作都一个接一个地按顺序持续执行.
在ETS中没有其他有效的支持来保证两个对象间的并发.但是,函数safe_fixtable/2可以用来保证first/1和next/2调用将正确得遍历全表,并且表中已存在的对象将准确地只作一次访问,即使其他进程(或当前进程)同时对表进行删除或插入操作.除些之外再无其他的保证.在遍历过程中被插入或删除的特殊对象将只访问一次或不访问.内部遍历表的函数,如select,match,与safe_fixtable有同样的保证.
匹配描述
一些函数要使用匹配描述match_spec.在select/2中有简短的说明.详细的描述请参阅ERTS用户指南的"Match specifications in Erlang".
数据类型
continuation()
Opaque continuation used by select/1 and select/3.
match_spec() = [{match_pattern(), [term()], [term()]}]
匹配描述,见前述。
match_pattern() = atom() | tuple()
tab() = atom() | tid()
tid()
一个表的标识标,由new/2返回
导出
all() -> [Tab]
Types:
Tab = tid() | atom()
返回当前节点中所有的表组成的列表。如果是命名表则给出表的名字,非命名表则给出表的标识符。
delete(Tab) -> true
Types:
Tab = tid() | atom()
删除整张表。
delete(Tab, Key) -> true
Types:
Tab = tid() | atom()
Key = term()
从表Tab中删除所有以Key为键的对象。
delete_all_objects(Tab) -> true
Types:
Tab = tid() | atom()
删除Tab表中所有的对象。该操作是原子和隔离的。
delete_object(Tab,Object) -> true
Types:
Tab = tid() | atom()
Object = tuple()
删除与Object精确匹配的对象,只有键相同,但其他有不匹配的对象不会被删除(这对于bag类型的表非常有用),在duplicate_bag表中,所有匹配的对象都会被删除。
file2tab(Filename) -> {ok, Tab} | {error, Reason}
Types:
Filename = file:name()
Tab = tab()
Reason = term()
读取一个由tab2file/2 或 tab2file/3生成的文件并创建对应的表,与file2tab(FileName, [])等效。
file2tab(Filename, Options) -> {ok, Tab} | {error, Reason}
Types:
Filename = file:name()
Tab = tab()
Options = [Option]
Option = {verify, boolean()}
Reason = term()
读取一个由tab2file/2 或 tab2file/3生成的文件并创建对应的表。目前支持的选项只有{verify, boolean()}.如果检验打开(也就是设置为{verify, true}),该函数将利用文件中的所有信息来断定数据是否被损坏,实现方式依赖于调用tab2file/3时写入extended_info的信息。如果文件中没有extended_info数据,但却设置了{verify,true},写入对象的数量将依赖于转储时原始表的大小。
如果在表是公共的并且在转储时有对象被删除或插入,将使得检验失败。为了避免这个问题,不要检验在转储的同时有更新的文件,或者在调用tab2file/3时使用{extended_info, [object_count]},这将会把实际写入的对象数记录到文件中。
如果开启检验并且转储时使用{extended_info, [md5sum]}选项,在读取文件时将会比其他情况慢,消耗更多的CPU时间。
Options的默认值为{verify,false}.
first(Tab) -> Key | '$end_of_table'
Types:
Tab = tid() | atom()
Key = term()
返回表中的第一个对象的键.如果表是ordered_set类型的,将返回erlang数据项顺序的第一个键,其他类型的表将返回表的内部顺序的第一个键.如果表为空,则返回'$end_of_table'。使用next/2可以获取表中后续的键.
foldl(Function, Acc0, Tab) -> Acc1
Types:
Function = fun((Element :: term(), AccIn) -> AccOut)
Tab = tab()
Acc0 = Acc1 = AccIn = AccOut = term()
如果表为空,则返回Acc0.对表中的每一个对象Elem调用Function(Elem,AccIn),第一次调用时,AccIn为Acc0,后续的每一次调用,AccIn为上一次调用的返回值,最终返回最后一次调用的结果.与lists:fold/3相似.
除了在ordered_set表中,遍历顺序将从first到last,其他类型的表遍历顺序不确定.如果在Function中或其他进程中插入对象到表里,这些对象有可能被遍历到(取决于键的次序).
foldr(Function, Acc0, Tab) -> Acc1
Types:
Function = fun((Element :: term(), AccIn) -> AccOut)
Tab = tab()
Acc0 = Acc1 = AccIn = AccOut = term()
与foldl/3相似,但ordered_set的遍历顺序是从last到first.
from_dets(Tab, DetsTab) -> true
Types:
Tab = tab()
DetsTab = dets:tab_name()
将一个打开的名为DetsTab的dets表的对象插入到已创建的ets表Tab中,Tab中原有的对象如果未被覆盖,将保留.如果有表不存在,或DetsTab未打开,将抛出badarg异常.
fun2ms(LiteralFun) -> MatchSpec
Types:
LiteralFun = function()
MatchSpec = match_spec()
使用词法转换器将作为参数传给函数的仿函数ListeralFun转换成匹配描述。“literal”意味着必须以fun的原本书写形式作为函数的参数,而不能将fun保存在变量中多次使用。词法转换功能在ms_transform模块中实现,而且源文件中必须引用STDLIB中的文件“ms_transform.hrl”才能使仿函数正常工作。没有包函此文件不会出现编译时错误,但会出现运行时错误。包函此文件很简单,只要在源文件中添加一行
-include_lib(“stdlib/include/ms_transform.hrl”)
即可。LiteralFun函数的限制很严格,只能有单一的参数(用于匹配的对象):一个单一的变量或元组。必须使用is_XXX断言检测。不允许出现匹配描述中没有的语法结构(如:if,case, receive等)。
返回值即为生成的匹配描述。
Example:
1> ets:fun2ms(fun({M,N}) when N > 3 -> M end).
[{{'$1','$2'},[{'>','$2',3}],['$1']}]
运行环境中的变量可以被引用,所以下面的做法是可行的:
2> X=3.
3
3> ets:fun2ms(fun({M,N}) when N > X -> M end).
[{{'$1','$2'},[{'>','$2',{const,3}}],['$1']}]
引用的变量将被匹配描述常量代替,这与Erlang fun的静态拷贝相一致。
在fun的断言或函数体内,不能有本地或全局函数调用,但可以调用erlang的内建函数:
4> ets:fun2ms(fun({M,N}) when N > X, is_atomm(M) -> M end).
Error: fun containing local Erlang function calls
('is_atomm' called in guard) cannot be translated into match_spec
{error,transform_error}
5> ets:fun2ms(fun({M,N}) when N > X, is_atom(M) -> M end).
[{{'$1','$2'},[{'>','$2',{const,3}},{is_atom,'$1'}],['$1']}]
give_away(Tab, Pid, GiftData) -> true
Types:
Tab = tid() | atom()
Pid = pid()
GiftData = term()
让Pid进程成为表Tab新的所属者。如果调用成功,信息{'ETS-TRANSFER', Tab, FromPid, GiftData}将会被发送到新的所属进程。Pid进程必须是本地存活进程并且并非Tab的原所属者。函数调用进程必须是Tab的所属进程。
give_away并不影响表的heir操作。例如,表的所属者可以将heir设成自己,放弃表的所有权,然后在表的新的所属进程崩溃时重新继承表的所属权。
i() -> ok
在标准输出设备打印所有ets表的信息
i(Tab) -> ok
Types:
Tab = tab()
在标准输出设备中打印Tab表的信息。
info(Tab) -> [{Item, Value}] | undefined
Types:
Tab = tid() | atom()
Item = atom(), see below
Value = term(), see below
返回表Tab的信息,结果以元组列表的形式--以元组{Item, Value}为元素。
如果Tab是表的标识符,但却没有关联一个存在的ETS表,则所回undefined,
如果Tab的类型不正确,函数以badarg的原因而失败。
Item=memory, Value=integer()
为表分配的字节数.
Item=owner, Value=pid()
表的所属进程的进程ID.
Item=heir, Value=pid()|none
表的后嗣进程ID,如果没有指定后嗣进程则为none
Item=name, Value=atom()
表的名字.
Item=size, Value=integer()
表中对象的数量.
Item=node, Value=atom()
表所在的结点。该字段已没有实际意义,因为表不能跨节点访问。
Item=named_table, Value=true|false
标识表是否是命名表.
Item=type, Value=set|ordered_set|bag|duplicate_bag
表的类型.
Item=keypos, Value=integer()
键的位置.
Item=protection, Value=public|protected|private
表的访问权限.
Item=compressed, Value=true|false
标识表是否压缩.
info(Tab, Item) -> Value | undefined
Types:
Tab = tid() | atom()
Item, Value - see below
返回表的指定项Item的信息,如果Tab没有与任何ETS表关联,则返回undefined.
如果表的类型错误或Item的值无效,函数将以badarg原因而失败。
init_table(Tab, InitFun) -> true
Types:
Tab = tab()
InitFun = fun((Arg) -> Res)
Arg = read | close
Res = end_of_input | {Objects :: [term()], InitFun} | term()
将调用InitFun创建的对象插入已存在的表Tab中,该函数主要是为了与DETS模块相通而写的,它并没有ets:insert/2高效。当Arg为read时,如果没有输入,则InitFun应该返回end_of_input,或{Objects, Fun},其中Objects是对象列表,Fun是新的输入函数。其他值Value将被放在错误信息{error,{init_fun, Value}}中被返回,每个输入函数只会被调用一次,如果发生错误,最后一个函数将会以close作为参数调用,返回值将被忽略。如果表的类型为set,并且同一个键有多个对象,只会选择其中一个。所以在输入函数的返回列表中,相同键的后续对象是没有必要的。这对bag类型的表中存储的重复对象一样适用。
insert(Tab, ObjectOrObjects) -> true
Types:
Tab = tid() | atom()
ObjectOrObjects = tuple() | [tuple()]
将对象Object或对对象列表Objects中的对象插入Tab表中,
如果表的类型为set,并且插入对象的键与已存在对象的键相匹配,则已存在对象将被覆盖。
如果表的类型为ordered_set,并且插入对象的键与已存在对象的键相等,则已存在对象将被覆盖。
如果对象列表中有多个对象具有相匹配的键,并且表是set类型的,只有一个对象会被插入,但不确定是哪个。
对于ordered_set类型的表,键相等时,情况相同。
整个操作是原子的且隔离的(即使有多个对象插入)。
insert_new(Tab, ObjectOrObjects) -> boolean()
Types:
Tab = tid() | atom()
ObjectOrObjects = tuple() | [tuple()]
该函数与insert/2很相似,但在遇到相同(相匹配或相等,对于set或ordered_set表)的键,或有多个对象的键已在表(bag或duplicate_bag)中时,函数返回false.如果参数是一个列表,函数在插入对象之前,将检测列表中所有的键,如果至少有一个键在表中已存在,则不插入任何对象。与insert/2一样,整个操作保证了原子性和隔离性。
is_compiled_ms(Term) -> boolean()
Types:
Term = term()
该函数用来检测一个已编译的匹配描述是否有效。已编译的匹配描述是一种不可传递的数据类型,不能在erlang节点之间传递,也不能保存到硬盘,任可试图构造一个外部形式的已编译匹配描述的操作都只能得到一个空的二进制数据(<<>>).
例如,下列表达式:
ets:is_compiled_ms(ets:match_spec_compile([{'_',[],[true]}])).
将返回true,而下列表达式:
MS = ets:match_spec_compile([{'_',[],[true]}]),
Broken = binary_to_term(term_to_binary(MS)),
ets:is_compiled_ms(Broken).
则返回false,因为Broken包含了通过外部形式传递而来的已编译匹配描述。
last(Tab) -> Key | '$end_of_table'
Types:
Tab = tid() | atom()
Key = term()
如果表的类型为ordered_st,则返回erlang数据项顺序的最后一个键,对于其他类型的表,该函数与first/2相似。
如果表为空,则返回'$end_of_table'.使用prev/2可以获取表中的前一个键。
lookup(Tab, Key) -> [Object]
Types:
Tab = tid() | atom()
Key = term()
Object = tuple()
返回Tab表中所有键为Key的对象的列表.在set,bat和duplicate_bag类型的表中,只有键与给定的键Key相匹配的对象才被返回.如果表的类型为ordered_set,只要对象的键与Key相等就会被返回.两者的不同点就像=:=和==的差别一样.比如,可以用整数1作为键插入一个对象到ordered_set类型的表中,然后用浮点数1.0作为键调用lookup/2来查找该对象.
如果表的类型为ordered_set或set,函数返回空列表或只有一个元素的列表,因为表中不可能有多个对象含有相同的键.
如果表的类型为bag或duplicate_bag,函数返回任意长度的列表(因为表中可能有多个对象含有相同的键).
返回对象的顺序与插入的时间顺序相同,即第一次插入的对象,返回时也排在第一位.
在set,bag和duplicate_bag类型的表插入和查找的时间是常量,与表的大小无关.而对于ordered_set类型的表,时间与表的大小的对数成正比.
lookup_element(Tab, Key, Pos) -> Elem
Types:
Tab = tid() | atom()
Key = term()
Pos = integer()
Elem = term() | [term()]
如果表的类型为set或ordered_set,函数返回键为Key的对象的第Pos个元素.如果表的类型为bag或duplicate_bag,函数返回所有键为Key的对象的第Pos个元素的列表.如果表中没有键为Key的对象,函数将以badarg为原因退出.
match(Tab, Pattern) -> [Match]
Types:
Tab = tid() | atom()
Pattern = tuple()
Match = [term()]
根据模式Pattern匹配表中的对象.一个模式是一个可能包含以下内容的数据项:
.绑定部分(erlang数据项);
.'_',匹配任何项目;
.模式变量'$N',N=0,1,...
函数返回一个列表,列表中的元素是每个匹配到的对象关于模式变量所绑定的元素的有序列表.
例如:
6> ets:match(T, '$1'). % 匹配表中所有的对象。
[[{rufsen,dog,7}],[{brunte,horse,5}],[{ludde,dog,5}]]
7> ets:match(T, {'_',dog,'$1'}).
[[7],[5]]
8> ets:match(T, {'_',cow,'$1'}).
[]
如果在模式中指定键,将使匹配非常高效。否则(比如,是一个模式变量,或'_'),匹配过程将要搜索全表,如果表很大的话,搜索时间会比较长。在ordered_set类型的表中,匹配结果的顺序与first/next遍历的顺序一样。
match(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'
Types:
Tab = tid() | atom()
Pattern = tuple()
Match = [term()]
Continuation = term()
与ets:match/2类似,但只返回有限个数(Limit)的匹配对象。Continuation项可在后续调用ets:match/1时作为参数来获取后续的匹配对象。这是一种操作多个对象时节省空间的方法,并且比用ets:first/1和ets:next/1逐个对象遍历全表还快速。如果表为空,则返回'$end_of_table'.
match(Continuation) -> {[Match],Continuation} | '$end_of_table'
Types:
Match = [term()]
Continuation = term()
继续匹配由ets:match/3开始的匹配。匹配到的对象(数量为最初调用ets:match/3时所指定的数量Limit)将与新的Continuation一起返回,新的Continuation将在后续调用调函数时被使用。如果表中已没有剩余的对象则返回'$end_of_table'.
match_delete(Tab, Pattern) -> true
Types:
Tab = tab()
Pattern = match_pattern()
匹配方式与match/2相同,但该函数将会把所有匹配到的对象从表中删除。
match_object(Tab, Pattern) -> [Object]
Types:
Tab = tid() | atom()
Pattern = Object = tuple()
匹配方式与match/2相同,但返回的结果是所有匹配对象的列表。
match_object(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'
Types:
Tab = tid() | atom()
Pattern = tuple()
Match = [term()]
Continuation = term()
与ets:match_object/2类似,但只返回有限个数(Limit)的匹配对象。Continuation项可在后续调用ets:match_object/1时作为参数来获取后续的匹配对象。这是一种操作多个对象时节省空间的方法,并且比用ets:first/1和ets:next/1逐个对象遍历全表还快速。如果表为空,则返回'$end_of_table'.
match_object(Continuation) -> {[Match],Continuation} | '$end_of_table'
Types:
Match = [term()]
Continuation = term()
继续匹配由ets:match_object/3开始的匹配。匹配到的对象(数量为最初调用ets:match_bject/3时所指定的数量Limit)将与新的Continuation一起返回,新的Continuation将在后续调用调函数时被使用。如果表中已没有剩余的对象则返回'$end_of_table'.
match_spec_compile(MatchSpec) -> CompiledMatchSpec
Types:
MatchSpec = match_spec()
CompiledMatchSpec = comp_match_spec()
该函数将一个匹配描述转换成在后续调用ets:match_spec_run/2中可以使用的内部形式.内部形式是非传递性的,不能转换成外部数据格式然后再原本的回传(这意味着不能将它发送给其他节点的进程,因为对方收到的将不是一个有效的已编译匹配描述,也不能将它保存到硬盘).可以用以下函数来检测一个已编译匹配描述的有效性:
ets:is_complied_ms/1.
如果MatchSpec不能被编译(不是一个有效的匹配描述),将抛出badarg异常.
match_spec_run(List,CompiledMatchSpec) -> list()
Types:
List = [ tuple() ]
CompiledMatchSpec = comp_match_spec()
该函数用指定的已编译匹配描述对List中的元组进行匹配.CompliedMatchSpec必须是调用ets:match_spec_compile/1所得的结果,所以CompliedMatchSpec也是匹配描述使用的内部形式。匹配将对List中的每个元素执行,然后返回包含所有匹配结果的列表。对于不匹配的元素,不会有任何项目返回,因此,返回的列表长度将小于等于List的长度。下列例子中的两次调用结果相同(但执行时间是不同的):
Table = ets:new...
MatchSpec = ....
% 下面的调用...
ets:match_spec_run(ets:tab2list(Table),
ets:match_spec_compile(MatchSpec)).
% ...将与下面的调用结果相同 (但下面的效率更高)
ets:select(Table,MatchSpec).
member(Tab, Key) -> true | false
Types:
Tab = tid() | atom()
Key = term()
与lookup/2相似,但并不是返回对象。如果表中至少有一个对象键为Key,函数返回true,否则返回false.
new(Name, Options) -> tid() | atom()
Types:
Name = atom()
Options = [Option]
Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks
Type = set | ordered_set | bag | duplicate_bag
Access = public | protected | private
Tweaks = {write_concurrency,boolean()} | {read_concurrency,boolean()} | compressed
Pos = integer()
HeirData = term()
创建一个新表,并返回可在后续操作中使用的表的标识符.表的标识符可以发送给其他进程,使表可在结点内的不同进程间共享.参数Options是一些原子的列表,指定了表的类型,访问权限,键的位置和表是否命名.未指定的选项将使用默认值.这意味着未指定任何选项([]),将与指定[set, protected, {keypos,1}, {heir,none}, {write_concurrency,false},{read_concurrency,false}]相同。
set 表的默认类型,一个键对应一个对象,对象之间是无序的。
ordered_set 一个键对应一个对象,以erlang数据项顺序排序,使用<和>作比较操作。此类型的表在某些情况下与其他类型的表表现出不同的行为。最明显的是,当两个键相同等或相匹配时,ordered_set都认为它们相 同。这意味着,对于ordered_set表,整数1和浮点数1.0是相同的。这也说明,如果表中的键混有浮点数和整数,查找使用的键与返回元素的键不一定要完全匹配,只要相等即可。
bag 此类型的表一个键可以对应多个对象,但每个对象只能对应一个键。
duplicate_bag 此类型的表一个键可对应多个对象,每个对象也可以对应多个键。
public 此种访问权限的表,任何进程都可以读写。
protected 此种访问权限的表,只有所属进程能写,其他进程只能读。这是表的默认访问权限。
private 此种访问权限的表,只有所属进程能读写表。
named_table 如果指定了该选项,名字Name将与表的标识符关联。在后续的操作中可以名字代替表的标识符。
{keypos,Pos} 指定原组中的哪个元素作为键。默认情况下,Pos为1.然而,有时这个值不太合适。在一些特殊情况 下,例如我们要在表中存贮erlang记录record时,不想用第一个元素作为键。注意,表中存放的任何元组都必须至少有Pos个元素。
{heir,Pid,HeirData} | {heir,none} 指定一个进程为表的后嗣进程。当所属进程崩溃时,后嗣进程将继承表的所有权。当这种情况发生时,消息{'ETS-TRANSFER',tid(),FromPid,HeirData}将会发送给后嗣进程。后嗣进程必须是本地进程。默认情况下,没有后嗣进程,当所属 进程崩溃时,表被销毁。
{write_concurrency,boolean()} 性能调优。默认为false,表的更新操作将独占访问权限,任何的并发的访问将被阻塞至更新操作结束。如果设为true,表将尽可能得允许并发写。表中不同的对象可以被进程并发读写。这比串行写并发读情况性能要好一点。write_concurrency选项可联合read_concurrency选项使用。当大量的并发读和并发写经常出现时,你通常要联合使作这两个选项(更详细的信息请阅读read_concurrency的说明)。这个选项不会改变任何的原子性和隔离性保证。具有这些保证的函数(如insert/2)将不会从该选项中获得优化。目前的版本中,该选项对ordered_set类型的表无效。
{read_concurrency,boolean()} 性能调优。默认为false。如果设为true,表将尽可能得允许并发读操作。在支持SMP的系统中设置该选项,读操作将变得非常高效,特别是在有多个物理处理器的系统中。然而,读写操作间的切换却会消耗更多的性能。当读操作比写操作更频繁,或者在大量的读写中有并发读写的情况时,你更加需要设置该选项(比如,大量的读操作不会被写操作中断,大量的写操作不会被读操作中断)
compressed 如果设置该选项,表中的数据将会存贮得更加紧凑以节省内存。缺点是,这会使表的 操作变慢。特别是要遍历全表的操作,如match和select,会更慢。目前的版本中,键元素不会被压缩。
next(Tab, Key1) -> Key2 | '$end_of_table'
Types:
Tab = tid() | atom()
Key1 = Key2 = term()
返回键Key1的下一个键Key2.如果表的类型是ordered_set,返回的是erlang数据项顺序的下一个键.其他类型的表,下一个键根据表内部顺序返回.如果表里已没有下一个键,则返回'$end_of_table'.可用first/1获取表的第一个键.对于类型为set,bag和duplicate_bat的表,如果有并发更新了表,遍历将失败,除非使用了safe_fixtable/2.而对于ordered_set类型的表,不管下一个键的对象是否还存在,都会返回下一个键。
prev(Tab, Key1) -> Key2 | '$end_of_table'
Types:
Tab = tid() | atom()
Key1 = Key2 = term()
返回键Key1的前一个键Key2。.如果表的类型是ordered_set,返回的是erlang数据项顺序的前一个键.其他类型的表,与next/2相似,返回的是表内顺序的前一个键.如果表里已没有前一个键,则返回'$end_of_table'.可用last/1获取表的最后一个键.
rename(Tab, Name) -> Name
Types:
Tab = Name = atom()
将已命名表Tab重命名为Name.此后,旧的名字不能再用来访问表。重命名对非命名表无效。
repair_continuation(Continuation, MatchSpec) -> Continuation
Types:
Continuation = continuation()
MatchSpec = match_spec()
如果调用ets:select/3或ets:select/1返回的非传递性延拓(continuation)是用外部数据格式传递而来的(在节点间发送或存盘),可调用该函数来修复非传递性延拓(continuation).实现该接口,是因为延拓数据项包含了已编译匹配描述,如果转换成外部数据格式将会失去有效性。给出原始的匹配描述,就可以修复延拓(continuation).这意味着,即使延拓保存硬盘或由其他节点传递而来,也可以在后续调用ets:select/1中再次使用。
例如, 下列的调用将失败:
T=ets:new(x,[]),
...
{_,C} = ets:select(T,ets:fun2ms(fun({N,_}=A)
when (N rem 10) =:= 0 ->
A
end),10),
Broken = binary_to_term(term_to_binary(C)),
ets:select(Broken).
但下列调用可以成功:
T=ets:new(x,[]),
...
MS = ets:fun2ms(fun({N,_}=A)
when (N rem 10) =:= 0 ->
A
end),
{_,C} = ets:select(T,MS,10),
Broken = binary_to_term(term_to_binary(C)),
ets:select(ets:repair_continuation(Broken,MS)).
因为调用ets:repari_continuation/2可以修复失效的延拓Broken.
safe_fixtable(Tab, true|false) -> true
Types:
Tab = tid() | atom()
锁定set,bag和duplicate_bag类型的表,确保遍历安全。进程通过调用safe_fixtable(Tab,true)来锁定一个表。直到进程调用safe_fixtable(Tab, false)或者进程结束,表才被释放。
如果有多个进程锁定表,表会一直保持锁定直到所有的进程都释放了表(或进程结束)。会有一个引用计算器,对每个进程做引用计数,N次连续锁定需要N次释放才能真正释放表。当表被锁定后,后续调用first/1和next/2会确保成功,并且表中的每个对象只会返回一次,即使在遍历的过程中有对象被删除或插入。在遍历的过程中插入的对象的键有可能被next/2返回(取决于键在表的内部顺序)。例如:
clean_all_with_value(Tab,X) ->
safe_fixtable(Tab,true),
clean_all_with_value(Tab,X,ets:first(Tab)),
safe_fixtable(Tab,false).
clean_all_with_value(Tab,X,'$end_of_table') ->
true;
clean_all_with_value(Tab,X,Key) ->
case ets:lookup(Tab,Key) of
[{Key,X}] ->
ets:delete(Tab,Key);
_ ->
true
end,
clean_all_with_value(Tab,X,ets:next(Tab,Key)).
注意,从锁定的表中删除对象并没有实际删除,直到表被释放才真正删除。如果一个进程锁定一个表,但一直没有释放,删除对象所使用的内存永远不会被释放。对表的操作的效率也会明显的下降。使用info/2可以获取哪个进程锁定了哪个表的相关信息。有大量的进程锁定表的系统需要一个监视器,在一个表被锁定的时间太长时发出警报。
对于ordered_set类型的表,不需要safe_fixtable/2,因为调用first/1和next/2总是成功。
select(Tab, MatchSpec) -> [Match]
Types:
Tab = tid() | atom()
Match = term()
MatchSpec = match_spec()
使用匹配描述从表中匹配对象.此函数调用比ets:match/2和ets:match_object/2更常用.以下是匹配描述的最简单形式:
MatchSpec = [MatchFunction]
MatchFunction = {MatchHead, [Guard], [Result]}
MatchHead = "Pattern as in ets:match"
Guard = {"Guardtest name", ...}
Result = "Term construct"
这意味着匹配描述总是含有一个以上元组元素的列表,且元组元素有三个成员,元组的第一个成员应是ets:match/2的文档中所描述的模式,第二个成员应是含0个或多个断言测试的列表,第三个成员应是包含关于实际返回值的描述的列表,通常是一个对返回值全描述的列表,即返回匹配对象的所有项目。返回值的结构使用MatchHead所绑定的”match variables”或者使用特殊的匹配值’$_’(整个对象)和”$$”(包含所有匹配值的列表),所以以下的ets:match/2表达式:
ets:match(Tab,{'$1','$2','$3'})
全等于
ets:select(Tab,[{{'$1','$2','$3'},[],['$$']}])
ets:match_object(Tab,{'$1','$2','$1'})
全等于
ets:select(Tab,[{{'$1','$2','$1'},[],['$_']}])
复杂的数据项也可以通过简单的写入列表来返回,所以以下代码:
ets:select(Tab,[{{'$1','$2','$3'},[],['$$']}])
与下面的表达式有同样的结果:
ets:select(Tab,[{{'$1','$2','$3'},[],[['$1','$2','$3']]}])
也就是将所有匹配头的绑定值放入列表中。如果要构造成元组,必须写成一个成员对应一个元素的元组放入待构造的元组中(因为普通的元组对于断言会报错),因些下列调用:
ets:select(Tab,[{{'$1','$2','$1'},[],['$_']}])
与下面的调用结果相同:
ets:select(Tab,[{{'$1','$2','$1'},[],[{{'$1','$2','$3'}}]}])
此句法等同于追踪模式中所使用的句法(见dbg(3))
断言是一个元组,第一个元素是测试名,其余的是测试的参数。检测匹配值’$1’的元素是不是一个指定类型(如list),应该将测试写成{is_list,’$1’},如果测试失败,对象就不会被匹配,然后尝试下一个匹配(如果有的话)。大部分erlang中现有的测试都可以使用,但只有新版本的前缀为is_的测试(如is_float, is_atom等)才可以使用。断言部分也可以包含逻辑和算术操作,写成与断言的句法一样(前缀表示法),所以下列erlang中的断言,
is_integer(X), is_integer(Y), X + Y < 4711
可以表述成这样(X replaced with '$1' and Y with '$2'):
[{is_integer, '$1'}, {is_integer, '$2'}, {'<', {'+', '$1', '$2'}, 4711}]
在ordered_set类型的表中,访问对象的顺序与first/next遍历一样。这意味着匹配描述将按first/next的顺序应用到对象上,并且返回的结果列表顺序与执行顺序相同.
select(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'
Types:
Tab = tid() | atom()
Match = term()
MatchSpec = match_spec()
Continuation = term()
与ets:select/2相似,但只返回限定数量(Limit)的匹配对象,Continuation项可在后调用ets:select/1时使用,来获取下一组的匹配对象。在操作表中的对象时,这是一种节省空间的方式,并且比通过调用ets:first/1和ets:next/1逐个访问对象还要快。如果表为空则返回’$end_of_table’.
select(Continuation) -> {[Match],Continuation} | '$end_of_table'
Types:
Match = term()
Continuation = term()
继续由ets:select/3开始的匹配. 匹配到的对象(数量为最初调用ets:select/3时所指定的数量Limit)将与新的Continuation一起返回,新的Continuation将在后续调用该函数时被使用。如果表中已没有剩余的对象则返回'$end_of_table'.
select_count(Tab, MatchSpec) -> NumMatched
Types:
Tab = tid() | atom()
Object = tuple()
MatchSpec = match_spec()
NumMatched = integer()
用匹配描述匹配Tab表中的对对象.如果对象使匹配描述返回true,则该对象被认为匹配并记数。对其他的返回值,对象都不会被认为匹配,也不会计数。该函数可以被描述成match_delete/2,但并不真正删除任务对象,只是计算数量。
select_delete(Tab, MatchSpec) -> NumDeleted
Types:
Tab = tid() | atom()
Object = tuple()
MatchSpec = match_spec()
NumDeleted = integer()
使用匹配描述在Tab表中匹配对象.如果对象使匹配描述返回true,则该对象将从表中移除,对于其他任何返回值,对象均会保留。该函数比ets:match_delete/2更常用。
函数返回实际从表中删除的对象的数量。
select_reverse(Tab, MatchSpec) -> [Match]
Types:
Tab = tid() | atom()
Match = term()
MatchSpec = match_spec()
跟select/2相似,但对于ordered_set表,返回值列表是反序的。
对于其他类型的表,返回值与select/2一样。
select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'
Types:
Tab = tid() | atom()
Match = term()
MatchSpec = match_spec()
Continuation = term()
与select/3相似,但对于ordered_set类型的表,遍历是从erlang数据项顺序的最后一个到第一个的。对于其他类型的表,返回值与select/3一样。
注意,这并不等同于反转select/3返回的结果,返回的结果不仅是反转的,而且是表中最后Limit个匹配的对象,而不是前面Limit个。
select_reverse(Continuation) -> {[Match],Continuation} | '$end_of_table'
Types:
Match = term()
Continuation = term()
继续匹配由ets:select_reverse/3开始的匹配,如果表是ordered_set类型的,遍历将指向键值在erlang数据项顺序中较前的对象,返回值列表同样是以键顺序反序的。对于其他类型的表,匹配行为与select/1相同。
Example:
1> T = ets:new(x,[ordered_set]).
2> [ ets:insert(T,{N}) || N <- lists:seq(1,10) ].
...
3> {R0,C0} = ets:select_reverse(T,[{'_',[],['$_']}],4).
...
4> R0.
[{10},{9},{8},{7}]
5> {R1,C1} = ets:select_reverse(C0).
...
6> R1.
[{6},{5},{4},{3}]
7> {R2,C2} = ets:select_reverse(C1).
...
8> R2.
[{2},{1}]
9> '$end_of_table' = ets:select_reverse(C2).
...
setopts(Tab, Opts) -> true
Types:
Tab = tid() | atom()
Opts = Opt | [Opt]
Opt = {heir,pid(),HeirData} | {heir,none}
HeirData = term()
设置表的选项。目前可以在表创建后设置的选项只有heir.调用进程必须是表的所属进程.
slot(Tab, I) -> [Object] | '$end_of_table'
Types:
Tab = tid() | atom()
I = integer()
Object = tuple()
该函数主要是用来做调试的。通常应该用first/next或last/prev代替.返回表中第I个位置的所有对象。可以通过反复地调用该函数,从第一个位置I=0开始到返回值为’$end_of_table’为止来遍历全表。如果参数I越界,函数将以badarg为原因而失败。对于set,bag和duplicate_bag类型的表,除非调用safe_fixtable/2锁表(见前述),否则当表有并发更新时,遍历会失败。如果表的类型为ordered_set,函数返回包含erlang数据项顺序的第I个对象的列表.
tab2file(Tab, Filename) -> ok | {error, Reason}
Types:
Tab = tab()
Filename = file:name()
Reason = term()
将Tab表导到文件Filename,等同与tab2file(Tab, Filename,[])
tab2file(Tab, Filename, Options) -> ok | {error, Reason}
Types:
Tab = tab()
Filename = file:name()
Options = [Option]
Option = {extended_info, [ExtInfo]}
ExtInfo = md5sum | object_count
Reason = term()
将Tab表导到文件Filename。导表时,一些关于表的必需的信息被导到头部,这些信息包括表的类型,名字,访问权限,大小,版本和是否已命名表,同时还有关于加到文件中的一些扩展信息相关注释,这些可以是文件中的对象数或者是头部和文件中record的MD5值.如果表的访问权限是public并且在导表的过程中有添加或删除对象,头部的大小字段值可能与实际的对象数不一致.public权限的表在导表时有更新,并且想在从文件中读取表时核对,则扩展信息中至少要有一个字段给读取和核对进程提供依据。
扩展信息选项指出有哪些扩展信息被写到了文件中:
object_count
实际写入文件的对象数量写在了文件的尾部,因此即使在导表时有更新表,也可以核对。
md5sum
头部和表中的对象使用内建的MD5函数来校验.所有对象的MD5值写在文件的尾部,所以读取时的校验可以发现文件中的数据微小的比特级的反转。使用该选项将要耗费适量的CPU时间。
一旦使用了扩展信息选项,将导致ets版本早于stdlib-1.15.1的文件无法读取.
tab2list(Tab) -> [Object]
Types:
Tab = tab()
Object = tuple()
返回包含表中所有对象的列表。注意:该函数需要遍历全表,慎用。
tabfile_info(Filename) -> {ok, TableInfo} | {error, Reason}
Types:
Filename = file:name()
TableInfo = [InfoItem]
InfoItem = {name, atom()}
| {type, Type}
| {protection, Protection}
| {named_table, boolean()}
| {keypos, integer() >= 0}
| {size, integer() >= 0}
| {extended_info, [ExtInfo]}
| {version,
{Major :: integer() >= 0,
Minor :: integer() >= 0}}
ExtInfo = md5sum | object_count
Type = bag | duplicate_bag | ordered_set | set
Protection = private | protected | public
Reason = term()
返回通过tab2file/2或tab2file/3导入文件的表的信息.
返回以下项目:
name
被导表的名字。如果表是已命名表,那么调用file2tab/2从文件中加载表时,不能存在相同名字的表。如果表没有保存为已命名表,那么从文件中加载表时,该选项无意义。
type
被导表的类型(可以是:set,bag,duplicate_bag,或者ordered_set).在加载表时会重新使用该类型
protection
被导表的访问权限(可以是private,protected或者public).从文件中加载的表将有相同的访问权限。
named_table
如果在导表是表是已命名表,则该项为true,否则为false.注意,从文件中加载已命名表时,系统中不能有相同名字的表存在。
keypos
被导表的键的位置,加载表时将再次使用
size
在开始导表时表中对象的数量,当表的访问权限为public时,不必和实际保存到文件的对象数一致,因为在导表的过程,其他进程可能添加或删除了对象。
extended_info
在文件的尾部写入扩展信息,以便从文件中加载表时做严格的检测,跟tab2file/3中指定的一样。该函数只是指出哪些信息会出现在文件尾部,并没有指定值。该项的值是一个包含一个或多个原子(oject_count和md5sum)的列表。
version
一个元组{Major,Minor},包含了ets 表导入文件的格式的主副版本号,该项目从stdlib-1.5.1开始加入,用旧版本导成的文件将返回{0,0}.
如果文件无法找到或严重损坏或并非调用tab2file/2或tab2file/3生成的文件,将返回error.
table(Tab) -> QueryHandle
table(Tab, Options) -> QueryHandle
Types:
Tab = tab()
QueryHandle = qlc:query_handle()
Options = [Option] | Option
Option = {n_objects, NObjects} | {traverse, TraverseMethod}
NObjects = default | integer() >= 1
TraverseMethod = first_next
| last_prev
| select
| {select, MatchSpec ::
match_spec()}
返回一个QLC(Query List Comprehension)查询句柄.qlc模块实现了一种主要基于Mnesia的查询语言, 但ETS,DETS和lists也可以作为源数据被qlc识别.调用ets:table/1,2 是使ETS表可以被qlc使用的方法。
当键的位置只有简单的约束时,QLC用ets:lookup/2查找键,但这样就不能遍历整个表。遍历操作依赖于这个操作如可实现:
first_next. 通过调用ets:first/1和ets:next/2逐个遍历表中的键
last_prev. 通过调用ets:last/1和ets:prev/2逐个遍历表中的键
select.通过调用ets:select/3和ets:select/1遍历全表.n_objects选项决定了返回对象的个数(select/3的第三个参数).默认是一次返回100个对象.匹配描述(select/3的第二个参数)由QLC重新组装:简单的过滤器转换成相等匹配描述,而复杂的过滤器则必须应用到通过select/3返回的所有对象上,且select/3的匹配描述是匹配所有对象的。
{select, MatchSpec}.跟select一样,通过调用select/3和select/1遍历全表.不同的是,明确给出了匹配描述。这里可以设定那些不能在QLC提供的句法中轻易表达的匹配描述。
下面例子中使用指定的匹配描述遍历表:
9> true = ets:insert(Tab = ets:new(t, []), [{1,a},{2,b},{3,c},{4,d}]),
MS = ets:fun2ms(fun({X,Y}) when (X > 1) or (X < 5) -> {Y} end),
QH1 = ets:table(Tab, [{traverse, {select, MS}}]).
使用内部匹配描述的例子:
10> QH2 = qlc:q([{Y} || {X,Y} <- ets:table(Tab), (X > 1) or (X < 5)]).
通过使用qlc:info/1可以证实,其实后面的例子与前面的等价。
11> qlc:info(QH1) =:= qlc:info(QH2).
true
qlc:info/1返回关于一个查询句柄的信息,对于两个相同的查询句柄将返回一致的信息。
test_ms(Tuple, MatchSpec) -> {ok, Result} | {error, Errors}
Types:
Tuple = tuple()
MatchSpec = match_spec()
Result = term()
Errors = [{warning | error, string()}]
该函数用来检测在调用ets:select/2时使用的匹配描述.它不仅检测MatchSpec的句法正确性,而且返回匹配描述应用到对象Tuple时的结果.如果匹配描述有错误,将返回{error,Errors},Errors是一个列表,列表中是用自然语言描述的匹配描述的错误。如果匹配描述在句法上正确,函数返回{ok, Result}, 其中Result是实际调用ets:select/2时返回的结果或者false—如果匹配描述不匹配对象Tuple的话。
这是一个很有用的调试和测试工具,特别是在编写复杂的ets:select/2调用时。
to_dets(Tab, DetsTab) -> DetsTab
Types:
Tab = tab()
DetsTab = dets:tab_name()
用已经打开的名为Tab的ETS表中的对对象填充一个已经创建或打开的Dets表。在对象被插入之前,Dets表将被置空.
update_counter(Tab, Key, UpdateOp) -> Result
update_counter(Tab, Key, [UpdateOp]) -> [Result]
update_counter(Tab, Key, Incr) -> Result
Types:
Tab = tid() | atom()
Key = term()
UpdateOp = {Pos,Incr} | {Pos,Incr,Threshold,SetValue}
Pos = Incr = Threshold = SetValue = Result = integer()
该函数提供了一种更新一个或多个计数器的有效方式,而免去了以下麻烦:查找对象->更新对象的元素->最后再把对象插入表。(更新操作是原子的,例如,在操作过程中其他进程不能访问该表)。
函数会通过增加Incr到第Pos个元素破坏性地更新表中键为Key的对象。新的计数值将被返回。如果没有指定元素的位置,则直接更新键所在位置的下一个元素。如果指定了Threshold(阀值),当下列情况发生时,计数器将被重置为SetValue:
Incr是非负数并且结果将大于Threshold;
Incr是负数并且结果将小于Threshold.
可以通过指定UpdateOp列表在对象中做多个更新操作.操作按照列表中指定的顺序执行。如果相同的计算位置在列表中多次出现,同一个计算器将会做多次更新,而且每次都会基于前一次的结果做更新操作。返回值是每次更新后新计算值组百的列表,顺序与操作列表中的一样.如果指定的是一个空列表,则不做任何更新并返回空列表。如果函数失败,则不做任何更新。给定的Key用于确定对象,在set类型的表中用于匹配对象的键,或在ordered_set中用于与对象的键作相等比较(不同点的详细描述请见lookup/2和new/2).
在以下情况中,函数将以badarg为原因而失败:
表的类型不是set或ordered_set,
不存在指定键的对象,
对象的元素个数不对,
更新的元素不是整数,
更新的元素本身又是键,
或者Pos,Incr,Threshold和SetValue中有非整数值。
update_element(Tab, Key, {Pos,Value}) -> true | false
update_element(Tab, Key, [{Pos,Value}]) -> true | false
Types:
Tab = tid() | atom()
Key = Value = term()
Pos = integer()
该函数提供了在对象中更新一个或多个元素的有效方法,而免去了以下麻烦:查找对象,更新,再将整个对象写回表中。它将破坏性得更新表中键为 Key的对象。第Pos个位置的元素的值将被设为Value.可以通过指定{Pos,Value}列表来同时更新一个对象中的多个元素。如果同一个位置在列表中多次出现,将使用列表中指定的最后一个值.如果列表为空,或者函数失败,则不做任何更新。该函数操作是原子的T并且理论上中间结果对其他进程不可见。如果找到了指定键的对象则返回true,否则返回false.
给定的Key用于确定对象,在set类型的表中用于匹配对象的键,或在ordered_set中用于与对象的键作相等比较(不同点的详细描述请见lookup/2和new/2).
在以下情况中,函数将以badarg为原因而失败:
表的类型不是set或ordered_set,
Pos小于1或大于对象的数量,
更新的元素本身又是键。