查看源代码 ets (stdlib v6.2)
内置的项存储。
这个模块是 Erlang 内置项存储 BIF 的接口。它们提供了在 Erlang 运行时系统中存储大量数据的能力,并可以实现对数据的恒定访问时间。(对于 ordered_set
,请参见下文,访问时间与存储对象的数量的对数成正比。)
数据组织成一组动态表,可以存储元组。每个表由一个进程创建。当进程终止时,表会自动销毁。每个表在创建时都设置了访问权限。
表分为四种不同的类型:set
、ordered_set
、bag
和 duplicate_bag
。set
或 ordered_set
表只能为每个键关联一个对象。bag
或 duplicate_bag
表可以为每个键关联多个对象。
在 set
类型表中,插入和查找时间是恒定的,与表的大小无关。对于 bag
和 duplicate_bag
类型,时间与具有相同键的对象数量成正比。即使看起来不相关的键,在查找感兴趣的键时,也可能导致跳过线性搜索(由于哈希冲突)。
警告
对于
bag
和duplicate_bag
类型的表,请避免插入大量具有相同键的对象。这会损害插入和查找性能,以及运行时环境的实时特性(哈希桶线性搜索不起作用)。
ordered_set
表类型使用二叉搜索树。插入和查找时间与表中对象的数量的对数成正比。
注意
在一个 Erlang 节点上存储的表数量曾经是有限制的。现在不再是这种情况(除了受内存使用限制)。之前的默认限制约为 1400 个表,可以通过设置环境变量
ERL_MAX_ETS_TABLES
或命令行选项+e
在启动 Erlang 运行时系统之前增加。这个硬性限制已被删除,但目前仍然设置ERL_MAX_ETS_TABLES
是有用的。它应该设置为使用的最大表数量的近似值,因为命名表的内部表是使用该值来确定大小的。如果使用了大量的命名表,而ERL_MAX_ETS_TABLES
没有增加,那么命名表的查找性能将会下降。
请注意,表没有自动垃圾回收。即使没有任何进程引用表,它也不会自动销毁,除非所有者进程终止。要显式销毁表,请使用函数 delete/1
。默认的所有者是创建该表的进程。要在进程终止时转移表的所有权,请使用选项 heir
或调用 give_away/3
。
一些实现细节
请注意匹配和比较相等之间的细微差别,这在 set
和 ordered_set
表类型中得到了体现。
- 如果两个 Erlang 项的类型相同且值相同,则它们
匹配
,因此1
匹配1
,但不匹配1.0
(因为1.0
是一个float/0
,而不是一个integer/0
)。 - 如果两个 Erlang 项的类型和值相同,或者如果两者都是数字类型且扩展为相同的值,则它们比较相等,因此
1
与1
和1.0
都比较相等。 ordered_set
基于 Erlang 项顺序工作,并且在扩展为相同值的integer/0
和float/0
之间不存在定义的顺序。因此,在ordered_set
表中,键1
和键1.0
被认为是相等的。
失败
此模块中的函数会引发错误异常,错误原因是:
badarg
- 如果任何参数的格式错误。badarg
- 如果表标识符无效。system_limit
- 修改值导致它在 VM 内部无法表示。例如,将计数器递增到超过可表示的最大整数。system_limit
- 如果作为参数传递的匹配规范的嵌套过多,导致调用进程在其上执行的调度器的调度器堆栈耗尽。调度器堆栈大小可以在启动运行时系统时进行配置。
并发
此模块为并发访问提供了一些有限的支持。对单个对象的所有更新都保证是原子和隔离的。这意味着对单个对象的更新操作要么完全成功,要么完全失败,没有任何影响(原子性),并且其他进程看不到更新的任何中间结果(隔离性)。一些更新多个对象的函数声明它们甚至保证整个操作的原子性和隔离性。在数据库术语中,隔离级别可以被视为“可序列化”,就好像所有隔离操作都是串行执行的,一个接一个地按严格的顺序执行。
表遍历
有不同的方法可以遍历表中的对象。
- 使用
first/1
、next/2
、last/1
和prev/2
,单步遍历,一次一个键。 - 使用
first_lookup/1
、next_lookup/2
、last_lookup/1
和prev_lookup/2
,单步遍历,一次一个键。当您还需要查找键的对象时,这更有效。 - 使用简单的匹配模式搜索,使用
match/1/2/3
、match_delete/2
和match_object/1/2/3
。 - 使用更强大的匹配规范搜索,使用
select/1/2/3
、select_count/2
、select_delete/2
、select_replace/2
和select_reverse/1/2/3
。 - 使用
tab2file/2/3
和tab2list/1
进行表转换。
如果表在遍历期间也由并发进程更新,则没有表遍历会保证整个表的一致快照。每个并发更新的对象的结果可能会被看到(或看不到),具体取决于遍历访问表的该部分时是否发生了更新。保证完整一致的表快照的唯一方法(如果您确实需要)是在整个遍历过程中禁止并发更新。
此外,在遍历期间插入或删除键的表上,以不安全的方式进行的遍历可能会产生以下不良影响:
- 任何键都可能被遗漏。
- 任何键都可能被找到多次。
- 如果删除了键,则遍历可能会失败,并出现
badarg
异常。
如果满足以下任一条件,则表遍历是安全的:
- 表的类型为
ordered_set
。 - 整个表遍历在一次 ETS 函数调用中完成。
- 使用函数
safe_fixtable/2
在整个遍历过程中保持表固定。
注意
即使始终保证单个对象的访问是 原子且隔离的,但每次通过表查找下一个键的遍历都不会有这样的保证。这通常不是问题,但如果并发进程在遍历期间插入对象,则可能会导致罕见的细微“意外”影响。例如,考虑一个进程执行以下操作:
ets:new(t, [ordered_set, named_table]), ets:insert(t, {1}), ets:insert(t, {2}), ets:insert(t, {3}),
另一个进程对
ets:first(t)
进行的并发调用,在极少数情况下可能会返回2
,即使2
从未以第一个键的顺序存在于表中。同样,对ets:next(t, 1)
的并发调用可能会返回3
,即使3
从未以紧接在1
之后的顺序存在于表中。这样的影响不太可能但有可能发生。如果未启用表选项
write_concurrency
,则此概率将进一步降低(如果不是消失)。这也只能是对遍历顺序有定义的ordered_set
的潜在担忧。
使用 match
和 select
函数的遍历可能不需要扫描整个表,具体取决于如何指定键。具有完全绑定键(没有任何匹配变量)的匹配模式将优化操作为单键查找,而无需任何表遍历。对于 ordered_set
,部分绑定键将限制遍历仅扫描表中基于项顺序的子集。部分绑定键是具有完全绑定的前缀的列表或元组。例如:
1> T = ets:new(t,[ordered_set]), ets:insert(T, {"555-1234", "John Smith"}).
true
2> %% Efficient search of all with area code 555
2> ets:match(T,{[$5,$5,$5,$- |'$1'],'$2'}).
[["1234","John Smith"]]
匹配规范
一些函数使用匹配规范,即 match_spec
。 简要说明请参阅 select/2
。 详细描述请参阅 ERTS 用户指南中的 Erlang 中的匹配规范 章节。
过度嵌套的匹配规范会导致引发 system_limit
错误异常。
摘要
函数
返回节点上所有表的列表。命名表由其名称指定,未命名表由其表标识符指定。
删除整个表 Table
。
从表 Table
中删除所有键为 Key
的对象。即使不存在键为 Key
的对象,此函数也会成功。
删除 ETS 表 Table
中的所有对象。操作保证是原子和隔离的。
从 ETS 表中删除确切的对象 Object
,保留键相同但其他方面不同的对象(对于 bag
类型很有用)。在 duplicate_bag
表中,会删除对象的所有实例。
读取由 tab2file/2
或 tab2file/3
生成的文件,并创建相应的表 Table
。
读取由 tab2file/2
或 tab2file/3
生成的文件,并创建相应的表 Table
。
返回表 Table
中的第一个键 Key
。对于 ordered_set
表,返回 Erlang 项顺序中的第一个键。对于其他表类型,返回表中内部顺序的第一个键。如果表为空,则返回 '$end_of_table'
。
如果表为空,则返回 Acc0
。此函数类似于 lists:foldl/3
。表元素以未指定的顺序遍历,除了 ordered_set
表,其中它们是先到后遍历。
如果表为空,则返回 Acc0
。此函数类似于 lists:foldr/3
。表元素以未指定的顺序遍历,除了 ordered_set
表,其中它们是后到先遍历。
用已打开的 Dets 表 DetsTab
中的对象填充已创建的 ETS 表。除非被覆盖,否则 ETS 表中现有的对象将被保留。
伪函数,通过 parse_transform
将函数调用中键入为参数的 LiteralFun
转换为 匹配规范。“文字”是指该 fun 必须以文本形式编写为函数的参数,它不能保存在变量中,而该变量又传递给该函数。
使进程 Pid
成为表 Table
的新所有者。如果成功,则将消息 {'ETS-TRANSFER',Table,FromPid,GiftData}
发送到新所有者。
在终端上显示有关所有 ETS 表的信息。
在终端上浏览表 Table
。
返回有关表 Table
的信息,以元组列表的形式。如果 Table
具有表标识符的正确类型,但不引用现有的 ETS 表,则返回 undefined
。如果 Table
的类型不正确,则会引发 badarg
异常。
返回与表 Table
的 Item
关联的信息,如果 Table
不引用现有的 ETS 表,则返回 undefined
。如果 Table
的类型不正确,或者 Item
不是允许的值之一,则会引发 badarg
异常。
用通过调用输入函数 InitFun
创建的对象替换表 Table
的现有对象,请参见下文。此函数是为了与 dets
模块兼容而提供的,它并不比使用 insert/2
填充表更有效。
将对象或列表 ObjectOrObjects
中的所有对象插入表 Table
。
与 insert/2
相同,不同之处在于,它不是覆盖具有相同键的对象(对于 set
或 ordered_set
),或者添加具有表中已存在的键的更多对象(对于 bag
和 duplicate_bag
),而是返回 false
。
检查一个项是否表示有效的已编译 匹配规范。已编译的匹配规范仅在调用 match_spec_compile/1
在其上编译的 Erlang 节点上有效。
根据 Erlang 项顺序返回 ordered_set
类型的表 Table
中的最后一个键 Key
。对于其他表类型,此函数与 first/1
同义。如果表为空,则返回 '$end_of_table'
。
返回表 Table
中所有键为 Key
的对象的列表。
对于类型为 set
或 ordered_set
的表 Table
,该函数返回键为 Key
的对象的第 Pos
个元素。
对于类型为 set
或 ordered_set
的表 Table
,该函数返回键为 Key
的对象的第 Pos
个元素。
将表 Table
中的对象与模式 Pattern
进行匹配。
从表 Table
中删除所有与模式 Pattern
匹配的对象。有关模式的描述,请参阅 match/2
。
继续使用 match_object/3
开始的匹配。返回初始 match_object/3
调用中指定大小的下一个块,以及一个新的 Continuation
,该 Continuation
可用于后续对此函数的调用。
将表 Table
中的对象与模式 Pattern
进行匹配。有关模式的描述,请参阅 match/2
。该函数返回与模式匹配的所有对象的列表。
工作方式与 match_object/2
类似,但仅返回有限 (Limit
) 数量的匹配对象。然后,可以在后续调用 match_object/1
时使用项 Continuation
来获取匹配对象的下一个块。这是一种在表中处理对象的节省空间的方法,它比使用 first/1
和 next/2
逐个遍历表对象更快。
将匹配规范转换为内部表示,该内部表示可用于后续对match_spec_run/2
的调用。内部表示是不透明的。要检查已编译匹配规范的有效性,请使用is_compiled_ms/1
。
在术语列表上执行已编译的匹配规范中指定的匹配。术语CompiledMatchSpec
应是调用match_spec_compile/1
的结果,因此是想要使用的匹配规范的内部表示。
与lookup/2
类似,但不返回对象。如果表中一个或多个元素具有键Key
,则返回true
,否则返回false
。
创建一个新表,并返回一个表标识符,该标识符可用于后续操作。表标识符可以发送到其他进程,以便可以在节点内的不同进程之间共享表。
返回表Table
中键Key1
之后的下一个键Key2
。对于表类型ordered_set
,将返回Erlang术语顺序中的下一个键。对于其他表类型,将返回表中内部顺序的下一个键。如果不存在下一个键,则返回'$end_of_table'
。
根据表Table
的ordered_set
类型中Erlang术语顺序,返回键Key1
之前的上一个键Key2
。对于其他表类型,该函数与next/2
同义。如果不存在上一个键,则返回'$end_of_table'
。
将已命名的表Table
重命名为新名称Name
。之后,旧名称不能用于访问表。重命名未命名的表不起作用。
使用first/1
和next/2
,match/3
和match/1
,match_object/3
和match_object/1
,或select/3
和select/1
,为安全遍历修复set
,bag
或duplicate_bag
类型的表。
使用匹配规范匹配表Table
中的对象。这是一个比match/2
和match_object/2
调用更通用的调用。最简单的形式,匹配规范如下
使用匹配规范匹配表Table
中的对象。如果且仅当匹配规范对对象返回true
时,该对象才被视为匹配并被计数。对于匹配规范的任何其他结果,该对象不被视为匹配,因此不被计数。
使用匹配规范匹配表Table
中的对象。如果且仅当匹配规范对对象返回true
时,该对象才从表中删除。对于匹配规范的任何其他结果,该对象将保留。这是一个比match_delete/2
更通用的函数。
使用匹配规范匹配表Table
中的对象。对于每个匹配的对象,现有对象将替换为匹配规范结果。
继续以select_reverse/3
开始的匹配。对于ordered_set
类型的表,对表的遍历将继续访问Erlang术语顺序中较早的键的对象。返回的列表还包含键的顺序相反的对象。对于所有其他表类型,其行为与select/1
完全相同。
设置表选项。创建表后允许设置的唯一选项是heir
。调用进程必须是表的所有者。
此函数主要用于调试目的,通常应使用first
/ next
或last
/ prev
代替。
将表Table
转储到文件Filename
。
将表Table
转储到文件Filename
。
返回表Table
中所有对象的列表。
返回由tab2file/2
或tab2file/3
转储到文件的表的相关信息。
等效于table/2
。
返回查询列表推导式 (QLC) 查询句柄。qlc
模块提供了一种主要针对 Mnesia 的查询语言,但 QLC 也将 ETS 表、Dets 表和列表识别为数据源。调用table/1,2
是将 ETS 表Table
用于 QLC 的方法。
返回并删除表Table
中具有键Key
的所有对象的列表。
将已创建/打开的 Dets 表填充到已打开的名为Table
的 ETS 表中的对象。在插入对象之前,会清空 Dets 表。
此函数提供了一种有效的方式来更新一个或多个计数器,而无需查找对象、通过递增元素来更新对象,然后将结果对象再次插入表中。该操作保证是原子且隔离的。
此函数提供了一种有效的方式来更新对象中的一个或多个元素,而无需查找、更新和写回整个对象。
类型
-type comp_match_spec() :: compiled_match_spec().
-opaque compiled_match_spec()
已编译的匹配规范。
-type continuation() :: '$end_of_table' | {table(), integer(), integer(), compiled_match_spec(), list(), integer()} | {table(), _, _, integer(), compiled_match_spec(), list(), integer(), integer()}.
select/1,3
、select_reverse/1,3
、match/1,3
和 match_object/1,3
使用的不透明延续。
-type match_spec() :: [{match_pattern(), [_], [_]}].
匹配规范,请参阅 匹配规范。
-type tab() :: table().
-type table_access() :: public | protected | private.
-type table_type() :: set | ordered_set | bag | duplicate_bag.
-opaque tid()
表标识符,由 new/2
返回。
函数
-spec all() -> [Table] when Table :: table().
返回节点上所有表的列表。命名表由其名称指定,未命名表由其表标识符指定。
不保证返回列表的一致性。在ets:all()
调用期间,由其他进程创建或删除的表可能包含在列表中,也可能不包含在列表中。仅保证包含/排除在调用ets:all()
之前创建/删除的表。
-spec delete(Table) -> true when Table :: table().
删除整个表 Table
。
从表 Table
中删除所有键为 Key
的对象。即使不存在键为 Key
的对象,此函数也会成功。
-spec delete_all_objects(Table) -> true when Table :: table().
删除 ETS 表 Table
中的所有对象。操作保证是原子和隔离的。
从 ETS 表中删除确切的对象 Object
,保留键相同但其他方面不同的对象(对于 bag
类型很有用)。在 duplicate_bag
表中,会删除对象的所有实例。
-spec file2tab(Filename) -> {ok, Table} | {error, Reason} when Filename :: file:name(), Table :: table(), Reason :: term().
读取由 tab2file/2
或 tab2file/3
生成的文件,并创建相应的表 Table
。
-spec file2tab(Filename, Options) -> {ok, Table} | {error, Reason} when Filename :: file:name(), Table :: table(), Options :: [Option], Option :: {verify, boolean()}, Reason :: term().
读取由 tab2file/2
或 tab2file/3
生成的文件,并创建相应的表 Table
。
唯一支持的选项是{verify,boolean()}
。如果启用了验证(通过指定{verify,true}
),则该函数会使用文件中存在的任何信息来断言该信息没有损坏。具体如何执行此操作取决于使用tab2file/3
写入的extended_info
。
如果文件中不存在extended_info
并且指定了{verify,true}
,则会将写入的对象数与开始转储时原始表的大小进行比较。如果表是public
,并且在将表转储到文件时添加或删除了对象,这可能会导致验证失败。要避免此问题,请不要验证在同时更新时转储的文件,或者使用选项{extended_info, [object_count]}
调用tab2file/3
,这会使用写入的对象数扩展文件中的信息。
如果启用验证并且文件是使用选项{extended_info, [md5sum]}
写入的,则读取文件会比其他情况下慢,并且会消耗更多的 CPU 时间。
{verify,false}
是默认值。
返回表 Table
中的第一个键 Key
。对于 ordered_set
表,返回 Erlang 项顺序中的第一个键。对于其他表类型,返回表中内部顺序的第一个键。如果表为空,则返回 '$end_of_table'
。
要查找表中的后续键,请使用 next/2
。
-spec first_lookup(Table) -> {Key, [Object]} | '$end_of_table' when Table :: table(), Key :: term(), Object :: tuple().
与 first/1
类似,但它返回存储在表中的对象以及键。这等效于执行 first/1
后跟 lookup/2
。如果表为空,则返回 '$end_of_table'
。
要查找表中的后续对象,请使用 next_lookup/2
。
-spec foldl(Function, Acc0, Table) -> Acc1 when Function :: fun((Element :: term(), AccIn) -> AccOut), Table :: table(), Acc0 :: term(), Acc1 :: term(), AccIn :: term(), AccOut :: term().
如果表为空,则返回 Acc0
。此函数类似于 lists:foldl/3
。表元素以未指定的顺序遍历,除了 ordered_set
表,其中它们是先到后遍历。
如果 Function
向表中插入对象,或者另一个进程向表中插入对象,这些对象可能(取决于键的顺序)包含在遍历中。
-spec foldr(Function, Acc0, Table) -> Acc1 when Function :: fun((Element :: term(), AccIn) -> AccOut), Table :: table(), Acc0 :: term(), Acc1 :: term(), AccIn :: term(), AccOut :: term().
如果表为空,则返回 Acc0
。此函数类似于 lists:foldr/3
。表元素以未指定的顺序遍历,除了 ordered_set
表,其中它们是后到先遍历。
如果 Function
向表中插入对象,或者另一个进程向表中插入对象,这些对象可能(取决于键的顺序)包含在遍历中。
-spec from_dets(Table, DetsTab) -> true when Table :: table(), DetsTab :: dets:tab_name().
用已打开的 Dets 表 DetsTab
中的对象填充已创建的 ETS 表。除非被覆盖,否则 ETS 表中现有的对象将被保留。
如果任何表不存在或者 Dets 表未打开,则会引发 badarg
异常。
-spec fun2ms(LiteralFun) -> MatchSpec when LiteralFun :: function(), MatchSpec :: match_spec().
伪函数,通过 parse_transform
将函数调用中键入为参数的 LiteralFun
转换为 匹配规范。“文字”是指该 fun 必须以文本形式编写为函数的参数,它不能保存在变量中,而该变量又传递给该函数。
解析转换在 ms_transform
模块中提供,并且源代码必须包含 STDLIB 中的文件 ms_transform.hrl
,此伪函数才能工作。如果源代码中未包含 hrl 文件,则会导致运行时错误,而不是编译时错误。最简单的包含方式是将行 -include_lib("stdlib/include/ms_transform.hrl").
添加到源文件中。
fun 非常受限,它只能接受一个参数(要匹配的对象):单个变量或元组。它必须使用 is_
保护测试。匹配规范中没有表示的语言结构(if
、case
、receive
等)是不允许的。
返回值是生成的匹配规范。
示例
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']}]
导入的变量会被匹配规范 const
表达式替换,这与 Erlang fun 的静态作用域一致。但是,局部或全局函数调用不能位于 fun 的保护子句或主体中。当然,允许调用内置的匹配规范函数
4> ets:fun2ms(fun({M,N}) when N > X, my_fun(M) -> M end).
Error: fun containing local Erlang function calls
('my_fun' 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']}]
如示例所示,该函数也可以从 shell 中调用。从 shell 中使用时,fun 必须字面上位于调用中。
警告
如果
parse_transform
没有应用于调用此伪函数的模块,则调用将在运行时失败(出现badarg
)。ets
模块导出一个具有此名称的函数,但除了在 shell 中使用该函数外,绝不能调用它。如果通过包含头文件ms_transform.hrl
正确应用了parse_transform
,则编译后的代码永远不会调用该函数,而是将函数调用替换为字面匹配规范。
有关详细信息,请参见 ms_transform
。
-spec give_away(Table, Pid, GiftData) -> true when Table :: table(), Pid :: pid(), GiftData :: term().
使进程 Pid
成为表 Table
的新所有者。如果成功,则将消息 {'ETS-TRANSFER',Table,FromPid,GiftData}
发送到新所有者。
进程 Pid
必须是活动的、本地的,并且还不是表的所有者。调用进程必须是表的所有者。
请注意,此函数不会影响表的选项 heir
。例如,表所有者可以将其 heir
设置为自身,将表移交出去,然后在接收者终止时将其取回。
-spec i() -> ok.
在终端上显示有关所有 ETS 表的信息。
-spec i(Table) -> ok when Table :: table().
在终端上浏览表 Table
。
-spec info(Table) -> InfoList | undefined when Table :: table(), InfoList :: [InfoTuple], InfoTuple :: {compressed, boolean()} | {decentralized_counters, boolean()} | {heir, pid() | none} | {id, tid()} | {keypos, pos_integer()} | {memory, non_neg_integer()} | {name, atom()} | {named_table, boolean()} | {node, node()} | {owner, pid()} | {protection, table_access()} | {size, non_neg_integer()} | {type, table_type()} | {write_concurrency, boolean()} | {read_concurrency, boolean()}.
返回有关表 Table
的信息,以元组列表的形式。如果 Table
具有表标识符的正确类型,但不引用现有的 ETS 表,则返回 undefined
。如果 Table
的类型不正确,则会引发 badarg
异常。
{compressed, boolean()}
- 指示表是否已压缩。{decentralized_counters, boolean()}
- 指示表是否使用decentralized_counters
。{heir, pid() | none}
- 表的继承者的 pid,如果没有设置继承者,则为none
。{id,
tid()
}
- 表标识符。{keypos, integer() >= 1}
- 键的位置。{memory, integer() >= 0}
- 为表分配的字数。{name, atom()}
- 表名。{named_table, boolean()}
- 指示表是否已命名。{node, node()}
- 存储表的节点。此字段不再有意义,因为无法从其他节点访问表。{owner, pid()}
- 表的所有者的 pid。{protection,
access()
}
- 表的访问权限。{size, integer() >= 0}
- 插入表中的对象数。{type,
type()
}
- 表的类型。{read_concurrency, boolean()}
- 指示表是否使用read_concurrency
。{write_concurrency, WriteConcurrencyAlternative}
- 指示表使用的write_concurrency
选项。
注意
此函数的执行时间受
decentralized_counters
表选项的影响。当decentralized_counters
选项设置为true
时,执行时间比decentralized_counters
选项设置为false
时长得多。
-spec info(Table, Item) -> Value | undefined when Table :: table(), Item :: binary | compressed | decentralized_counters | fixed | heir | id | keypos | memory | name | named_table | node | owner | protection | safe_fixed | safe_fixed_monotonic_time | size | stats | type | write_concurrency | read_concurrency, Value :: term().
返回与表 Table
的 Item
关联的信息,如果 Table
不引用现有的 ETS 表,则返回 undefined
。如果 Table
的类型不正确,或者 Item
不是允许的值之一,则会引发 badarg
异常。
除了为 info/1
定义的 {Item,Value}
对之外,还允许以下项
Item=binary, Value=BinInfo
BinInfo
是一个列表,其中包含有关表所保留的二进制文件的各种信息。此Item
可以更改或删除,恕不另行通知。在当前实现中,BinInfo
是元组{BinaryId,BinarySize,BinaryRefcCount}
的列表。Item=fixed, Value=boolean()
指示表是否被任何进程固定。
Item=safe_fixed|safe_fixed_monotonic_time, Value={FixationTime,Info}|false
如果表使用
safe_fixtable/2
固定,则调用返回一个元组,其中FixationTime
是表从未固定变为固定的最后时间。FixationTime
的格式和值取决于Item
safe_fixed
-FixationTime
对应于固定时erlang:timestamp/0
返回的结果。请注意,当系统使用单或多 时间扭曲模式 时,这可能会产生奇怪的结果,因为使用safe_fixed
不是 时间扭曲安全 的。时间扭曲安全代码必须改用safe_fixed_monotonic_time
。safe_fixed_monotonic_time
-FixationTime
对应于固定时erlang:monotonic_time/0
返回的结果。使用safe_fixed_monotonic_time
是 时间扭曲安全 的。
Info
是一个可能为空的元组列表{Pid,RefCount}
,每个进程固定表时都有一个元组。RefCount
是引用计数器的值,它跟踪进程固定表的次数。表固定不限于
safe_fixtable/2
。例如,遍历函数(如select
和match
)也可能会进行临时固定。此类表固定会在相应的函数返回之前自动释放,但是并发调用ets:info(T,safe_fixed|safe_fixed_monotonic_time)
可能会看到它们。如果表根本没有固定,则调用返回
false
。Item=stats, Value=tuple()
返回有关表的内部统计信息,格式为 OTP 测试套件使用的内部格式。不用于生产。
注意
当函数的第二个参数是
size
或memory
时,此函数的执行时间受decentralized_counters
表选项的影响。当decentralized_counters
选项设置为true
时,执行时间比decentralized_counters
选项设置为false
时长得多。
-spec init_table(Table, InitFun) -> true when Table :: table(), InitFun :: fun((Arg) -> Res), Arg :: read | close, Res :: end_of_input | {Objects :: [term()], InitFun} | term().
用通过调用输入函数 InitFun
创建的对象替换表 Table
的现有对象,请参见下文。此函数是为了与 dets
模块兼容而提供的,它并不比使用 insert/2
填充表更有效。
当使用参数 read
调用时,如果不再有输入,则假定函数 InitFun
返回 end_of_input
,或者返回 {Objects, Fun}
,其中 Objects
是对象列表,而 Fun
是新的输入函数。任何其他值 Value
都将作为错误 {error, {init_fun, Value}}
返回。每个输入函数只被调用一次,如果发生错误,则最后调用的函数将使用参数 close
调用,其答复将被忽略。
如果表类型为 set
,并且存在多个具有给定键的对象,则选择其中一个对象。这不一定是输入函数返回的对象序列中具有给定键的最后一个对象。对于存储在 bag
类型的表中的重复对象,情况也是如此。
-spec insert(Table, ObjectOrObjects) -> true when Table :: table(), ObjectOrObjects :: tuple() | [tuple()].
将对象或列表 ObjectOrObjects
中的所有对象插入表 Table
。
- 如果表类型为
set
,并且插入对象的键匹配表中任何对象的键,则会替换旧对象。 - 如果表类型为
ordered_set
,并且插入对象的键与表中任何对象的键比较相等,则会替换旧对象。 - 如果表类型为
bag
,并且该对象匹配表中的任何整个对象,则不会插入该对象。 - 如果列表中包含多个具有匹配键的对象,并且表类型为
set
,则会插入一个对象,具体插入哪一个对象是未定义的。如果键比较相等,则表类型ordered_set
也适用。
即使插入对象列表,也保证整个操作是 原子且隔离的。
对于 bag
和 duplicate_bag
,列表中具有相同键的对象将按列表顺序插入(从头到尾)。也就是说,后续调用 lookup(T,Key)
将按插入顺序返回它们。
注意
对于
bag
,上面描述的相同键的插入顺序在 OTP 23.0 中被意外地反转,并在 OTP 25.3 中得到修复。也就是说,从 OTP 23.0 到 OTP 25.3,列表中的对象按相反的顺序插入(从尾到头)。对于
duplicate_bag
,从 OTP 23.0 到 OTP 25.3 存在相同的错误反向插入问题。但是,它是不可预测的,可能发生也可能不发生。较长的列表会增加反向插入的可能性。
-spec insert_new(Table, ObjectOrObjects) -> boolean() when Table :: table(), ObjectOrObjects :: tuple() | [tuple()].
与 insert/2
相同,不同之处在于,它不是覆盖具有相同键的对象(对于 set
或 ordered_set
),或者添加具有表中已存在的键的更多对象(对于 bag
和 duplicate_bag
),而是返回 false
。
如果 ObjectOrObjects
是一个列表,该函数会在插入任何内容之前检查每个键。除非列表中存在的所有键都不在表中,否则不会插入任何内容。与 insert/2
类似,整个操作保证是 原子和隔离的。
检查一个项是否表示有效的已编译 匹配规范。已编译的匹配规范仅在调用 match_spec_compile/1
在其上编译的 Erlang 节点上有效。
注意
在 STDLIB 3.4 (OTP 20.0) 之前,编译后的匹配规范没有外部表示。如果通过
binary_to_term(term_to_binary(CMS))
传递或发送到另一个节点再返回,结果始终是一个空二进制<<>>
。在 STDLIB 3.4 (OTP 20.0) 之后,编译后的匹配规范具有外部表示,作为对原始编译的匹配规范的节点特定引用。如果通过
binary_to_term(term_to_binary(CMS))
传递或发送到另一个节点再返回,结果可能是一个有效的编译后的匹配规范,具体取决于原始编译的匹配规范是否仍然存活。
根据 Erlang 项顺序返回 ordered_set
类型的表 Table
中的最后一个键 Key
。对于其他表类型,此函数与 first/1
同义。如果表为空,则返回 '$end_of_table'
。
要查找表中前面的键,请使用 prev/2
。
-spec last_lookup(Table) -> {Key, [Object]} | '$end_of_table' when Table :: table(), Key :: term(), Object :: tuple().
与 last/1
类似,但它返回存储在表中的对象以及键。这等效于执行 last/1
后跟 lookup/2
。如果表为空,则返回 '$end_of_table'
。
要查找表中前面的对象,请使用 prev_lookup/2
。
返回表 Table
中所有键为 Key
的对象的列表。
- 对于类型为
set
、bag
或duplicate_bag
的表,仅当指定的键与表中对象的键匹配时,才会返回对象。 - 对于类型为
ordered_set
的表,如果指定的键与表中对象的键比较相等,则返回对象。
区别与 =:=
和 ==
之间的区别相同。
例如,可以在 ordered_set
中插入一个键为 integer/0
1
的对象,并使用键为 float/0
1.0
进行 lookup/2
查找,从而返回该对象。
对于类型为 set
或 ordered_set
的表,该函数返回空列表或包含一个元素的列表,因为不能存在多个具有相同键的对象。对于类型为 bag
或 duplicate_bag
的表,该函数返回任意长度的列表。
请注意,对象插入的顺序被保留;使用指定键插入的第一个对象是结果列表中的第一个对象,依此类推。另请参阅关于 列表插入顺序 的说明。
-spec lookup_element(Table, Key, Pos) -> Elem when Table :: table(), Key :: term(), Pos :: pos_integer(), Elem :: term() | [term()].
对于类型为 set
或 ordered_set
的表 Table
,该函数返回键为 Key
的对象的第 Pos
个元素。
对于类型为 bag
或 duplicate_bag
的表,该函数返回一个列表,其中包含每个键为 Key
的对象的第 Pos
个元素。
如果不存在键为 Key
的对象,则该函数将以 badarg
为原因退出。
如果 Pos
大于元组的大小,则该函数将以 badarg
为原因退出。
关于 ordered_set
在比较相等时将键视为相等,而其他表类型仅在键匹配时才将其视为相等,set
、bag
和 duplicate_bag
与 ordered_set
之间的差异,对于 lookup_element/3
仍然成立。
-spec lookup_element(Table, Key, Pos, Default) -> Elem when Table :: table(), Key :: term(), Pos :: pos_integer(), Default :: term(), Elem :: term() | [term()].
对于类型为 set
或 ordered_set
的表 Table
,该函数返回键为 Key
的对象的第 Pos
个元素。
对于类型为 bag
或 duplicate_bag
的表,该函数返回一个列表,其中包含每个键为 Key
的对象的第 Pos
个元素。
如果不存在键为 Key
的对象,则该函数返回 Default
。
如果 Pos
大于任何具有匹配键的元组的大小,则该函数将以 badarg
为原因退出。
关于 ordered_set
在比较相等时将键视为相等,而其他表类型仅在键匹配时才将其视为相等,set
、bag
和 duplicate_bag
与 ordered_set
之间的差异,对于 lookup_element/4
仍然成立。
-spec match(Continuation) -> {[Match], Continuation} | '$end_of_table' when Match :: [term()], Continuation :: continuation().
继续使用 match/3
开始的匹配。返回初始 match/3
调用中指定大小的下一个块,以及一个新的 Continuation
,该 Continuation
可用于后续对此函数的调用。
当表中没有更多对象时,返回 '$end_of_table'
。
-spec match(Table, Pattern) -> [Match] when Table :: table(), Pattern :: match_pattern(), Match :: [term()].
将表 Table
中的对象与模式 Pattern
进行匹配。
模式是可以包含以下内容的项
- 绑定的部分(Erlang 项)
'_'
,它匹配任何 Erlang 项- 模式变量
'$N'
,其中N
=0,1,...
该函数返回一个列表,其中包含每个匹配对象的一个元素,其中每个元素都是一个有序的模式变量绑定列表,例如
6> ets:match(T, '$1'). % Matches every object in table
[[{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
遍历中的顺序相同。
-spec match(Table, Pattern, Limit) -> {[Match], Continuation} | '$end_of_table' when Table :: table(), Pattern :: match_pattern(), Limit :: pos_integer(), Match :: [term()], Continuation :: continuation().
工作方式与 match/2
类似,但仅返回有限 (Limit
) 数量的匹配对象。然后,可以在后续调用 match/1
时使用项 Continuation
来获取匹配对象的下一个块。这是一种在表中处理对象的节省空间的方法,它比使用 first/1
和 next/2
逐个遍历表对象更快。
如果表为空,则返回 '$end_of_table'
。
使用 safe_fixtable/2
来保证对 match/1
的后续调用进行 安全遍历。
-spec match_delete(Table, Pattern) -> true when Table :: table(), Pattern :: match_pattern().
从表 Table
中删除所有与模式 Pattern
匹配的对象。有关模式的描述,请参阅 match/2
。
-spec match_object(Continuation) -> {[Object], Continuation} | '$end_of_table' when Object :: tuple(), Continuation :: continuation().
继续使用 match_object/3
开始的匹配。返回初始 match_object/3
调用中指定大小的下一个块,以及一个新的 Continuation
,该 Continuation
可用于后续对此函数的调用。
当表中没有更多对象时,返回 '$end_of_table'
。
-spec match_object(Table, Pattern) -> [Object] when Table :: table(), Pattern :: match_pattern(), Object :: tuple().
将表 Table
中的对象与模式 Pattern
进行匹配。有关模式的描述,请参阅 match/2
。该函数返回与模式匹配的所有对象的列表。
如果在模式中指定了键,则匹配非常高效。如果未指定键,即如果它是变量或下划线,则必须搜索整个表。如果表非常大,则搜索时间可能会很长。
对于类型为 ordered_set
的表,结果的顺序与 first
/next
遍历中的顺序相同。
-spec match_object(Table, Pattern, Limit) -> {[Object], Continuation} | '$end_of_table' when Table :: table(), Pattern :: match_pattern(), Limit :: pos_integer(), Object :: tuple(), Continuation :: continuation().
工作方式与 match_object/2
类似,但仅返回有限 (Limit
) 数量的匹配对象。然后,可以在后续调用 match_object/1
时使用项 Continuation
来获取匹配对象的下一个块。这是一种在表中处理对象的节省空间的方法,它比使用 first/1
和 next/2
逐个遍历表对象更快。
如果表为空,则返回 '$end_of_table'
。
使用 safe_fixtable/2
来保证对 match_object/1
的后续调用进行 安全遍历。
-spec match_spec_compile(MatchSpec) -> CompiledMatchSpec when MatchSpec :: match_spec(), CompiledMatchSpec :: compiled_match_spec().
将匹配规范转换为内部表示,该内部表示可用于后续对match_spec_run/2
的调用。内部表示是不透明的。要检查已编译匹配规范的有效性,请使用is_compiled_ms/1
。
如果项 MatchSpec
不表示有效的匹配规范,则会引发 badarg
异常。
注意
此函数在普通代码中的用途有限。它由
dets
模块用于执行dets:select/1
操作。
-spec match_spec_run(List, CompiledMatchSpec) -> list() when List :: [term()], CompiledMatchSpec :: compiled_match_spec().
在术语列表上执行已编译的匹配规范中指定的匹配。术语CompiledMatchSpec
应是调用match_spec_compile/1
的结果,因此是想要使用的匹配规范的内部表示。
匹配在 List
中的每个元素上执行,该函数返回一个包含所有结果的列表。如果 List
中的元素不匹配,则不会返回该元素的任何内容。因此,结果列表的长度等于或小于参数 List
的长度。
示例
以下两个调用给出相同的结果(但执行时间肯定不同)
Table = ets:new...
MatchSpec = ...
% The following call...
ets:match_spec_run(ets:tab2list(Table),
ets:match_spec_compile(MatchSpec)),
% ...gives the same result as the more common (and more efficient)
ets:select(Table, MatchSpec),
注意
此函数在普通代码中的用途有限。它由
dets
模块用于执行dets:select/1
操作,并由 Mnesia 在事务期间使用。
与lookup/2
类似,但不返回对象。如果表中一个或多个元素具有键Key
,则返回true
,否则返回false
。
-spec new(Name, Options) -> table() when Name :: atom(), Options :: [Option], Option :: Type | Access | named_table | {keypos, Pos} | {heir, Pid :: pid(), HeirData} | {heir, none} | Tweaks, Type :: table_type(), Access :: table_access(), WriteConcurrencyAlternative :: boolean() | auto, Tweaks :: {write_concurrency, WriteConcurrencyAlternative} | {read_concurrency, boolean()} | {decentralized_counters, boolean()} | compressed, Pos :: pos_integer(), HeirData :: term().
创建一个新表,并返回一个表标识符,该标识符可用于后续操作。表标识符可以发送到其他进程,以便可以在节点内的不同进程之间共享表。
参数 Options
是一个选项列表,用于指定表类型、访问权限、键位置以及表是否已命名。省略选项时使用默认值。这意味着不指定任何选项 ([]
) 与指定 [set, protected, {keypos,1}, {heir,none}, {write_concurrency,false}, {read_concurrency,false}, {decentralized_counters,false}]
相同。
set
- 表是一个set
表:一个键,一个对象,对象之间没有顺序。这是默认的表类型。ordered_set
- 表是一个ordered_set
表:一个键,一个对象,按照 Erlang 项顺序排序,这是 < 和 > 运算符隐含的顺序。这种类型的表在某些情况下与其他类型的表略有不同。最值得注意的是,ordered_set
表在比较相等时将键视为相等,而不仅在它们匹配时才视为相等。这意味着对于ordered_set
表,integer/0
1
和float/0
1.0
被视为相等。这也意味着用于查找元素的键不一定与返回元素中的键匹配,如果在表的键中混合了float/0
和integer/0
。bag
- 表是一个bag
表,它可以有多个对象,但每个键只有一个对象实例。duplicate_bag
- 表是一个duplicate_bag
表,它可以有多个对象,包括每个键的同一对象的多个副本。public
- 任何进程都可以读取或写入该表。protected
- 所有者进程可以读取和写入该表。其他进程只能读取该表。这是访问权限的默认设置。private
- 只有所有者进程可以读取或写入该表。named_table
- 如果存在此选项,则该表将以其Name
注册,然后可以在后续操作中使用该名称而不是表标识符。该函数还将返回
Name
而不是表标识符。要获取已命名表的表标识符,请使用whereis/1
。{keypos,Pos}
- 指定在存储的元组中使用哪个元素作为键。默认情况下,它是第一个元素,即Pos=1
。但是,这并不总是合适的。特别是,如果我们要将 Erlang 记录存储在表中,我们不希望第一个元素作为键。请注意,存储在表中的任何元组都必须至少具有
Pos
个元素。{heir,Pid,HeirData} | {heir,none}
- 将一个进程设置为继承者。如果所有者终止,则继承者会继承该表。发生这种情况时,消息{'ETS-TRANSFER',tid(),FromPid,HeirData}
会发送给继承者。继承者必须是一个本地进程。默认继承者是none
,它会在所有者终止时销毁该表。{write_concurrency,WriteConcurrencyAlternative}
- 性能调优。默认为false
,在这种情况下,修改(写入)表的操作会获得独占访问权限,阻止对同一表的任何并发访问,直到操作完成。如果设置为true
,则该表针对并发写入访问进行了优化。同一个表的不同对象可以由并发进程修改(和读取)。在一定程度上,这是以内存消耗以及顺序访问和并发读取的性能为代价实现的。write_concurrency
选项的auto
替代方案类似于true
选项,但在运行时会根据表的使用方式自动调整同步粒度。当使用 Erlang/OTP 25 及更高版本时,这是推荐的write_concurrency
选项,因为它在大多数情况下表现良好。write_concurrency
选项可以与选项read_concurrency
和decentralized_counters
结合使用。当大量并发读取和大量并发写入很常见时,通常需要将write_concurrency
与read_concurrency
结合使用;有关更多信息,请参见选项read_concurrency
。将write_concurrency
选项与decentralized_counters
选项结合使用几乎总是一个好主意。请注意,此选项不会更改关于原子性和隔离性的任何保证。对多个对象做出此类保证的函数(如
insert/2
)从此选项中获得的收益较少(或没有)。当不使用
write_concurrency
选项的true
替代方案时,write_concurrency
和read_concurrency
都会对set
、bag
和duplicate_bag
表产生每个表的恒定开销。对于所有使用auto
替代方案的表以及使用true
替代方案的ordered_set
表,内存开销取决于运行时检测到的实际并发量。当write_concurrency
和read_concurrency
结合使用时,内存开销可能特别大。注意
在 stdlib-3.7 (OTP-22.0) 之前,
write_concurrency
对ordered_set
没有影响。注意
write_concurrency
选项的auto
替代方案仅在 OTP-25.0 及更高版本中可用。{read_concurrency,boolean()}
(自 OTP R14B 起)
性能调优。默认为false
。当设置为true
时,该表针对并发读取操作进行了优化。启用此选项后,读取操作的成本会降低很多;尤其是在具有多个物理处理器的系统上。但是,在读写操作之间切换的成本会更高。当并发读取操作比写入操作频繁得多,或者当并发读写以大量读取和写入突发的形式出现时(即,许多读取操作不会被写入操作中断,而许多写入操作不会被读取操作中断),通常需要启用此选项。
当常见的访问模式是少量读取操作与少量写入操作反复交错时,通常不需要启用此选项。在这种情况下,启用此选项会导致性能下降。
选项
read_concurrency
可以与选项write_concurrency
结合使用。当大量并发读取和大量并发写入很常见时,通常需要将它们结合使用。{decentralized_counters,boolean()}
(自 OTP 23.0 起)
性能调优。对于所有write_concurrency
选项设置为auto
的表,默认为true
。对于ordered_set
类型的表,当write_concurrency
选项设置为true
时,该选项也默认为 true。对于所有其他配置,该选项默认为false
。如果write_concurrency
选项设置为false
,则此选项无效。当此选项设置为
true
时,该表针对频繁并发调用修改表大小和/或内存消耗的操作进行了优化(例如,insert/2
和delete/2
)。缺点是,当启用decentralized_counters
选项时,使用size
或memory
作为第二个参数调用info/1
和info/2
可能会慢得多。启用此选项后,表大小和内存消耗的计数器将分布在多个缓存行上,并且调度线程将映射到其中一个缓存行。
erl
选项+dcg
可用于控制计数器分布的缓存行数。compressed
(自 OTP R14B01 起)
如果存在此选项,则表数据将以更紧凑的格式存储,以减少内存消耗。但是,这会使表操作变慢。尤其是需要检查整个对象的操作(如match
和select
)会慢很多。键元素不会被压缩。
-spec next(Table, Key1) -> Key2 | '$end_of_table' when Table :: table(), Key1 :: term(), Key2 :: term().
返回表Table
中键Key1
之后的下一个键Key2
。对于表类型ordered_set
,将返回Erlang术语顺序中的下一个键。对于其他表类型,将返回表中内部顺序的下一个键。如果不存在下一个键,则返回'$end_of_table'
。
要查找表中的第一个键,请使用 first/1
。
除非使用 safe_fixtable/2
固定 set
、bag
或 duplicate_bag
类型的表,否则如果 Key1
不再存在于表中,则调用 next/2
将失败。对于 ordered_set
表类型,该函数始终按术语顺序返回 Key1
之后的下一个键,无论 Key1
是否曾经存在于表中。
-spec next_lookup(Table, Key1) -> {Key2, [Object]} | '$end_of_table' when Table :: table(), Key1 :: term(), Key2 :: term(), Object :: tuple().
与next/2
类似,不同之处在于它返回存储在表中的键以及对象。这等效于执行next/2
,然后执行lookup/2
。如果不存在下一个键,则返回'$end_of_table'
。
它可以在遍历期间与 next/2
交错使用。
-spec prev(Table, Key1) -> Key2 | '$end_of_table' when Table :: table(), Key1 :: term(), Key2 :: term().
根据表Table
的ordered_set
类型中Erlang术语顺序,返回键Key1
之前的上一个键Key2
。对于其他表类型,该函数与next/2
同义。如果不存在上一个键,则返回'$end_of_table'
。
要查找 ordered_set
表中的最后一个键,请使用 last/1
。
-spec prev_lookup(Table, Key1) -> {Key2, [Object]} | '$end_of_table' when Table :: table(), Key1 :: term(), Key2 :: term(), Object :: tuple().
与prev/2
类似,不同之处在于它返回存储在表中的键以及对象。这等效于执行prev/2
,然后执行lookup/2
。如果不存在上一个键,则返回'$end_of_table'
。
它可以在遍历期间与 prev/2
交错使用。
将已命名的表Table
重命名为新名称Name
。之后,旧名称不能用于访问表。重命名未命名的表不起作用。
-spec repair_continuation(Continuation, MatchSpec) -> Continuation when Continuation :: continuation(), MatchSpec :: match_spec().
如果延续符已通过外部术语格式(已在节点之间发送或存储在磁盘上),则恢复由select/3
或select/1
返回的不透明延续符。
此函数的原因是,延续项包含已编译的匹配规范,如果转换为外部术语格式,则可能会失效。鉴于原始匹配规范保持不变,可以恢复延续项,这意味着即使它已存储在磁盘上或另一个节点上,也可以再次在后续的 select/1
调用中使用。
示例
以下调用序列可能会失败
T=ets:new(x,[]),
...
MS = ets:fun2ms(fun({N,_}=A) when (N rem 10) =:= 0 -> A end),
{_,C} = ets:select(T, MS, 10),
MaybeBroken = binary_to_term(term_to_binary(C)),
ets:select(MaybeBroken).
以下序列有效,因为调用 repair_continuation/2
会重新建立 MaybeBroken
延续项。
T=ets:new(x,[]),
...
MS = ets:fun2ms(fun({N,_}=A) when (N rem 10) =:= 0 -> A end),
{_,C} = ets:select(T,MS,10),
MaybeBroken = binary_to_term(term_to_binary(C)),
ets:select(ets:repair_continuation(MaybeBroken,MS)).
注意
此函数在应用程序代码中很少需要。Mnesia 使用它来提供分布式
select/3
和select/1
序列。普通的应用程序要么使用 Mnesia,要么保持延续项不转换为外部格式。从外部格式重新创建时,已编译的匹配规范的实际行为已更改,并且在未来的版本中可能会更改,但此接口仍保留以实现向后兼容性。请参见
is_compiled_ms/1
。
使用first/1
和next/2
,match/3
和match/1
,match_object/3
和match_object/1
,或select/3
和select/1
,为安全遍历修复set
,bag
或duplicate_bag
类型的表。
进程通过调用 safe_fixtable(Table, true)
来固定表。表保持固定状态,直到进程通过调用 safe_fixtable(Table, false)
释放它,或者直到进程终止。
如果多个进程固定一个表,则该表将保持固定状态,直到所有进程都释放它(或终止)。每个进程保留一个引用计数器,并且 N 个连续的固定需要 N 个释放才能释放该表。
当表被固定时,即使在遍历期间删除了键,也可以保证 first/1
和 next/2
调用的序列成功。在遍历期间插入或删除的对象的键可能会或可能不会由 next/2
返回,具体取决于表中键的顺序以及在调用 next/2
时键是否存在。
示例
clean_all_with_value(Table,X) ->
safe_fixtable(Table,true),
clean_all_with_value(Table,X,ets:first(Table)),
safe_fixtable(Table,false).
clean_all_with_value(Table,X,'$end_of_table') ->
true;
clean_all_with_value(Table,X,Key) ->
case ets:lookup(Table,Key) of
[{Key,X}] ->
ets:delete(Table,Key);
_ ->
true
end,
clean_all_with_value(Table,X,ets:next(Table,Key)).
请注意,已删除的对象不会从固定的表中释放,直到该表被释放。如果进程固定了一个表但从未释放它,则已删除对象使用的内存将永远不会被释放。表上操作的性能也会显着降低。
要检索有关哪些进程固定了哪些表的信息,请使用 info(Table, safe_fixed_monotonic_time)
。具有许多固定表的进程的系统可能需要一个监视器,以便在表固定时间过长时发送警报。
请注意,对于 ordered_set
表类型以及由单个 ETS 函数调用(如 select/2
)完成的遍历,safe_fixtable/2
不是必需的。
-spec select(Continuation) -> {[Match], Continuation} | '$end_of_table' when Match :: term(), Continuation :: continuation().
继续以select/3
开始的匹配。返回初始select/3
调用中指定大小的下一个块,以及可用于后续调用此函数的新Continuation
。
当表中没有更多对象时,返回 '$end_of_table'
。
-spec select(Table, MatchSpec) -> [Match] when Table :: table(), MatchSpec :: match_spec(), Match :: term().
使用匹配规范匹配表Table
中的对象。这是一个比match/2
和match_object/2
调用更通用的调用。最简单的形式,匹配规范如下
MatchSpec = [MatchFunction]
MatchFunction = {MatchHead, [Guard], [Result]}
MatchHead = "Pattern as in ets:match"
Guard = {"Guardtest name", ...}
Result = "Term construct"
这意味着匹配规范始终是一个或多个元组(arity 为 3)的列表。元组的第一个元素是 match/2
中描述的模式。元组的第二个元素是包含 0 个或多个保护测试的列表(如下所述)。元组的第三个元素是包含返回值的描述的列表。在几乎所有正常情况下,该列表都只包含一个完全描述每个对象返回值的项。
返回值是使用在 MatchHead
中绑定的“匹配变量”或者使用特殊的匹配变量 '$_'
(整个匹配对象)和 '$$'
(列表中所有的匹配变量)构造的,因此以下 match/2
表达式
ets:match(Table,{'$1','$2','$3'})
与以下表达式完全等效
ets:select(Table,[{{'$1','$2','$3'},[],['$$']}])
并且以下 match_object/2
调用
ets:match_object(Table,{'$1','$2','$1'})
与以下表达式完全等效
ets:select(Table,[{{'$1','$2','$1'},[],['$_']}])
复合项可以在 Result
部分中通过简单地编写一个列表来构造,因此以下代码
ets:select(Table,[{{'$1','$2','$3'},[],['$$']}])
产生与以下代码相同的输出
ets:select(Table,[{{'$1','$2','$3'},[],[['$1','$2','$3']]}])
即,匹配头中所有绑定变量的列表。如果要构造元组,则必须编写一个 arity 为 1 的元组,其中元组中的单个元素是要构造的元组(因为普通的元组可能会被误认为是 Guard
)。
因此,以下调用
ets:select(Table,[{{'$1','$2','$1'},[],['$_']}])
产生与以下代码相同的输出
ets:select(Table,[{{'$1','$2','$1'},[],[{{'$1','$2','$3'}}]}])
此语法等效于 Runtime_Tools 中 dbg
模块中使用的跟踪模式语法。
Guard
被构造为元组,其中第一个元素是测试名称,其余元素是测试参数。要检查绑定到匹配变量 '$1'
的元素的特定类型(例如列表),可以编写测试为 {is_list, '$1'}
。如果测试失败,则表中的对象不匹配,并尝试下一个 MatchFunction
(如果有)。可以使用 Erlang 中的大多数保护测试,但只允许使用带有 is_
前缀的新版本(is_float
、is_atom
等)。
Guard
部分还可以包含逻辑和算术运算,它们使用与保护测试相同的语法(前缀表示法)编写,因此在 Erlang 中编写的以下保护测试
is_integer(X), is_integer(Y), X + Y < 4711
表示如下(X
替换为 '$1'
,Y
替换为 '$2'
)
[{is_integer, '$1'}, {is_integer, '$2'}, {'<', {'+', '$1', '$2'}, 4711}]
对于类型为 ordered_set
的表,对象的访问顺序与 first
/next
遍历中的顺序相同。这意味着匹配规范是针对具有 first
/next
顺序键的对象执行的,并且相应的结果列表的顺序与该执行顺序相同。
-spec select(Table, MatchSpec, Limit) -> {[Match], Continuation} | '$end_of_table' when Table :: table(), MatchSpec :: match_spec(), Limit :: pos_integer(), Match :: term(), Continuation :: continuation().
与select/2
类似,但只返回有限的(Limit
)数量的匹配对象。然后,术语Continuation
可用于后续调用select/1
以获取匹配对象的下一个块。这是一种在表上高效地处理对象的方式,它仍然比使用first/1
和next/2
逐个遍历表对象更快。
如果表为空,则返回 '$end_of_table'
。
使用 safe_fixtable/2
以保证后续调用 select/1
的安全遍历。
-spec select_count(Table, MatchSpec) -> NumMatched when Table :: table(), MatchSpec :: match_spec(), NumMatched :: non_neg_integer().
使用匹配规范匹配表Table
中的对象。如果且仅当匹配规范对对象返回true
时,该对象才被视为匹配并被计数。对于匹配规范的任何其他结果,该对象不被视为匹配,因此不被计数。
该函数返回匹配对象的数量。
-spec select_delete(Table, MatchSpec) -> NumDeleted when Table :: table(), MatchSpec :: match_spec(), NumDeleted :: non_neg_integer().
使用匹配规范匹配表Table
中的对象。如果且仅当匹配规范对对象返回true
时,该对象才从表中删除。对于匹配规范的任何其他结果,该对象将保留。这是一个比match_delete/2
更通用的函数。
该函数返回从表中删除的对象数量。
注意
如果需要删除对象,则匹配规范必须返回原子
true
。没有其他返回值会删除该对象。因此,不能使用相同的匹配规范来查找元素和删除元素。
-spec select_replace(Table, MatchSpec) -> NumReplaced when Table :: table(), MatchSpec :: match_spec(), NumReplaced :: non_neg_integer().
使用匹配规范匹配表Table
中的对象。对于每个匹配的对象,现有对象将替换为匹配规范结果。
保证每个单独对象的匹配和替换操作是原子和隔离的。像所有其他选择函数一样,整个 select_replace
表遍历不提供这种保证。
必须保证匹配规范保留任何匹配对象的键。否则,select_replace
将会失败,并返回 badarg
,而不更新任何对象。
目前,由于性能和语义限制,尚不支持 bag
类型的表。
该函数返回替换对象的总数。
示例
对于第二个位置有列表的所有 2 元组,在列表的开头添加原子 'marker'
1> T = ets:new(x,[]), ets:insert(T, {key, [1, 2, 3]}).
true
2> MS = ets:fun2ms(fun({K, L}) when is_list(L) -> {K, [marker | L]} end).
[{{'$1','$2'},[{is_list,'$2'}],[{{'$1',[marker|'$2']}}]}]
3> ets:select_replace(T, MS).
1
4> ets:tab2list(T).
[{key,[marker,1,2,3]}]
一个通用的单对象比较和交换操作
[Old] = ets:lookup(T, Key),
New = update_object(Old),
Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
-spec select_reverse(Continuation) -> {[Match], Continuation} | '$end_of_table' when Continuation :: continuation(), Match :: term().
继续以select_reverse/3
开始的匹配。对于ordered_set
类型的表,对表的遍历将继续访问Erlang术语顺序中较早的键的对象。返回的列表还包含键的顺序相反的对象。对于所有其他表类型,其行为与select/1
完全相同。
示例
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).
...
-spec select_reverse(Table, MatchSpec) -> [Match] when Table :: table(), MatchSpec :: match_spec(), Match :: term().
与select/2
类似,但对于ordered_set
类型的表,以相反的顺序返回列表。对于所有其他表类型,返回值与select/2
的返回值相同。
-spec select_reverse(Table, MatchSpec, Limit) -> {[Match], Continuation} | '$end_of_table' when Table :: table(), MatchSpec :: match_spec(), Limit :: pos_integer(), Match :: term(), Continuation :: continuation().
与select/3
类似,但是对于ordered_set
类型的表,遍历从Erlang术语顺序中的最后一个对象开始,并移动到第一个对象。对于所有其他表类型,返回值与select/3
的返回值相同。
请注意,这与反转 select/3
调用的结果列表不等效,因为结果列表不仅被反转,而且还包含表中最后 Limit
个匹配对象,而不是第一个。
-spec setopts(Table, Opts) -> true when Table :: table(), Opts :: Opt | [Opt], Opt :: {heir, pid(), HeirData} | {heir, none}, HeirData :: term().
设置表选项。创建表后允许设置的唯一选项是heir
。调用进程必须是表的所有者。
-spec slot(Table, I) -> [Object] | '$end_of_table' when Table :: table(), I :: non_neg_integer(), Object :: tuple().
此函数主要用于调试目的,通常应使用first
/ next
或last
/ prev
代替。
返回表 Table
的槽 I
中的所有对象。可以通过重复调用该函数来遍历表,从第一个槽 I=0
开始,并在返回 '$end_of_table'
时结束。如果参数 I
超出范围,则该函数将因 badarg
失败。
除非使用 safe_fixtable/2
保护 set
、bag
或 duplicate_bag
类型的表,否则如果对该表进行并发更新,则遍历可能会失败。对于表类型 ordered_set
,该函数会返回一个列表,其中包含按 Erlang 术语顺序排列的对象 I
。
-spec tab2file(Table, Filename) -> ok | {error, Reason} when Table :: table(), Filename :: file:name(), Reason :: term().
将表Table
转储到文件Filename
。
-spec tab2file(Table, Filename, Options) -> ok | {error, Reason} when Table :: table(), Filename :: file:name(), Options :: [Option], Option :: {extended_info, [ExtInfo]} | {sync, boolean()}, ExtInfo :: md5sum | object_count, Reason :: term().
将表Table
转储到文件Filename
。
转储表时,有关该表的一些信息会转储到转储开头的标头中。此信息包含有关表类型、名称、保护、大小、版本以及是否为命名表的数据。它还包含关于向文件中添加哪些扩展信息的注释,这些信息可以是文件中对象的计数或文件中标题和记录的 MD5 校验和。
如果表是公共的,并且在转储期间从表中添加或删除记录,则标头中的 size 字段可能与文件中的记录数不对应。在转储期间更新的公共表,如果想在读取时进行验证,则需要至少一个扩展信息字段,以使后续读取验证过程可靠。
选项 extended_info
指定要写入表转储的额外信息
object_count
- 写入文件的对象数在文件页脚中记录,因此即使在转储期间更新了文件,也可以验证文件截断。md5sum
- 文件中的标头和对象使用内置的 MD5 函数进行校验。所有对象的 MD5 校验和都写入文件页脚,以便在读取时验证检测文件数据中最轻微的位翻转。使用这个需要相当多的 CPU 时间。
每当使用选项 extended_info
时,都会导致 ETS 1.15.1 之前的 STDLIB 版本无法读取该文件
如果选项 sync
设置为 true
,则确保在 tab2file
返回之前将文件内容写入磁盘。默认为 {sync, false}
。
返回表Table
中所有对象的列表。
-spec tabfile_info(Filename) -> {ok, TableInfo} | {error, Reason} when Filename :: file:name(), TableInfo :: [InfoItem], InfoItem :: {name, atom()} | {type, Type} | {protection, Protection} | {named_table, boolean()} | {keypos, non_neg_integer()} | {size, non_neg_integer()} | {extended_info, [ExtInfo]} | {version, {Major :: non_neg_integer(), Minor :: non_neg_integer()}}, 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
- 转储表的 ETS 类型(即,set
、bag
、duplicate_bag
或ordered_set
)。该类型在再次加载表时使用。protection
- 转储表的保护级别(即,private
、protected
或public
)。从文件加载的表将获得相同的保护级别。named_table
- 如果该表在转储到文件时是命名表,则为true
,否则为false
。请注意,当从文件加载命名表时,系统中不能存在同名的表。keypos
- 转储到文件的表的keypos
,在再次加载该表时使用。size
- 表转储到文件开始时,表中对象的数量。对于public
表,此数字不需要与保存到文件中的对象数量相对应,因为在表转储期间,另一个进程可能已经添加或删除了对象。extended_info
- 写入文件页脚的扩展信息,以便在从文件加载表时进行更强的验证,如tab2file/3
所指定的那样。请注意,此函数仅告知存在哪些信息,而不是文件页脚中的值。该值是一个列表,其中包含一个或多个原子object_count
和md5sum
。version
- 一个元组{Major,Minor}
,包含 ETS 表转储文件格式的主版本号和次版本号。此版本字段从 STDLIB 1.5.1 开始添加。使用旧版本转储的文件在此字段中返回{0,0}
。
如果文件无法访问、严重损坏或不是通过 tab2file/2
或 tab2file/3
生成的,则会返回错误。
-spec table(Table) -> QueryHandle when Table :: table(), QueryHandle :: qlc:query_handle().
等效于table/2
。
-spec table(Table, Options) -> QueryHandle when Table :: table(), QueryHandle :: qlc:query_handle(), Options :: [Option] | Option, Option :: {n_objects, NObjects} | {traverse, TraverseMethod}, NObjects :: default | pos_integer(), TraverseMethod :: first_next | last_prev | select | {select, MatchSpec :: match_spec()}.
返回查询列表推导式 (QLC) 查询句柄。qlc
模块提供了一种主要针对 Mnesia 的查询语言,但 QLC 也将 ETS 表、Dets 表和列表识别为数据源。调用table/1,2
是将 ETS 表Table
用于 QLC 的方法。
当键位置只有简单限制时,QLC 使用 lookup/2
来查找键。当不可能时,将遍历整个表。选项 traverse
确定如何执行此操作。
select
- 通过调用select/3
和select/1
遍历表。选项n_objects
确定返回的对象数量(select/3
的第三个参数);默认情况下,一次返回100
个对象。匹配规范(select/3
的第二个参数)由 QLC 组装:简单的过滤器被转换为等效的匹配规范,而更复杂的过滤器必须应用于select/3
返回的所有对象,给定一个匹配所有对象的匹配规范。{select, MatchSpec}
- 与select
相同,通过调用select/3
和select/1
遍历表。不同之处在于匹配规范是显式指定的。这是一种声明无法在 QLC 提供的语法中轻松表达的匹配规范的方法。
示例
这里使用显式匹配规范来遍历表
9> true = ets:insert(Table = 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(Table, [{traverse, {select, MS}}]).
一个使用隐式匹配规范的示例
10> QH2 = qlc:q([{Y} || {X,Y} <- ets:table(Table), (X > 1) or (X < 5)]).
后一个示例与前一个示例等效,可以使用函数 qlc:info/1
进行验证
11> qlc:info(QH1) =:= qlc:info(QH2).
true
qlc:info/1
返回有关查询句柄的信息,在本例中,两个查询句柄返回相同的信息。
返回并删除表Table
中具有键Key
的所有对象的列表。
指定的 Key
用于通过以下方式识别对象:在 ordered_set
表中比较相等对象的键,或者在其他类型的表中进行匹配(有关差异的详细信息,请参阅 lookup/2
和 new/2
)。
-spec test_ms(Tuple, MatchSpec) -> {ok, Result} | {error, Errors} when Tuple :: tuple(), MatchSpec :: match_spec(), Result :: term(), Errors :: [{warning | error, string()}].
此函数是一个实用程序,用于测试在对select/2
的调用中使用的匹配规范。该函数既测试MatchSpec
的“语法”正确性,又针对对象Tuple
运行匹配规范。
如果匹配规范在语法上正确,则该函数返回 {ok,Result}
,其中 Result
将是实际 select/2
调用中的结果,如果匹配规范与对象 Tuple
不匹配,则返回 false
。
如果匹配规范包含错误,则返回元组 {error, Errors}
,其中 Errors
是描述匹配规范中错误原因的自然语言列表。
这是一个有用的调试和测试工具,尤其是在编写复杂的 select/2
调用时。
另请参阅:erlang:match_spec_test/3
。
-spec to_dets(Table, DetsTab) -> DetsTab when Table :: table(), DetsTab :: dets:tab_name().
将已创建/打开的 Dets 表填充到已打开的名为Table
的 ETS 表中的对象。在插入对象之前,会清空 Dets 表。
-spec update_counter(Table, Key, UpdateOp | [UpdateOp] | Incr) -> Result | [Result] when Table :: table(), Key :: term(), UpdateOp :: {Pos, Incr} | {Pos, Incr, Threshold, SetValue}, Pos :: integer(), Incr :: integer(), Threshold :: integer(), SetValue :: integer(), Result :: integer().
等效于update_counter/4
。
-spec update_counter(Table, Key, UpdateOp | Incr | [UpdateOp], Default) -> Result | [Result] when Table :: table(), Key :: term(), UpdateOp :: {Pos, Incr} | {Pos, Incr, Threshold, SetValue}, Pos :: integer(), Incr :: integer(), Threshold :: integer(), SetValue :: integer(), Result :: integer(), Default :: tuple().
此函数提供了一种有效的方式来更新一个或多个计数器,而无需查找对象、通过递增元素来更新对象,然后将结果对象再次插入表中。该操作保证是原子且隔离的。
此函数通过将 Incr
添加到位置 Pos
的元素,以破坏性的方式更新表 Table
中键为 Key
的对象。返回新的计数器值。如果未指定位置,则更新紧跟键 (<keypos>+1
) 的元素。
如果指定了 Threshold
,则在发生以下情况时,计数器将重置为值 SetValue
Incr
不为负 (>= 0
) 且结果将大于 (>
)Threshold
。Incr
为负 (< 0
) 且结果将小于 (<
)Threshold
。
可以提供一个 UpdateOp
列表来在对象中执行多个更新操作。这些操作按照列表中指定的顺序执行。如果同一个计数器位置在列表中出现多次,则相应的计数器会被多次更新,每次都基于先前的结果。返回值是列表中每个更新操作的新计数器值的列表,顺序与操作列表中的顺序相同。如果指定了空列表,则不会更新任何内容,并且返回一个空列表。如果函数失败,则不会进行任何更新。
指定的 Key
用于通过以下方式识别对象:在 set
表中匹配对象的键,或者在 ordered_set
表中比较等于对象的键(有关差异的详细信息,请参阅 lookup/2
和 new/2
)。
如果指定了默认对象 Default
,则当表中缺少键时,它将用作要更新的对象。忽略键位置的值,并替换为正确的键值。返回值就像未使用默认对象一样,即单个更新的元素或它们的列表。
在以下情况下,函数会因 badarg
而失败
- 表类型不是
set
或ordered_set
。 - 不存在具有正确键的对象,并且未提供默认对象。
- 对象的元数错误。
- 默认对象的元数小于
<keypos>
。 - 默认对象中任何已更新的字段都不是整数。
- 要更新的元素不是整数。
- 要更新的元素也是键。
Pos
、Incr
、Threshold
或SetValue
中的任何一个都不是整数。
-spec update_element(Table, Key, ElementSpec) -> boolean() when Table :: table(), Key :: term(), ElementSpec :: {Pos, Value} | [{Pos, Value}], Pos :: pos_integer(), Value :: term().
等效于update_element/4
。
-spec update_element(Table, Key, ElementSpec, Default) -> true when Table :: table(), Key :: term(), ElementSpec :: {Pos, Value} | [{Pos, Value}], Pos :: pos_integer(), Value :: term(), Default :: tuple().
此函数提供了一种有效的方式来更新对象中的一个或多个元素,而无需查找、更新和写回整个对象。
此函数以破坏性的方式更新表 Table
中键为 Key
的对象。位置 Pos
的元素被赋予值 Value
。
可以提供 {Pos,Value}
列表以更新同一对象中的多个元素。如果列表中同一位置出现多次,则写入列表中的最后一个值。如果列表为空或函数失败,则不会进行任何更新。该函数也是原子的,这意味着其他进程永远看不到任何中间结果。
如果找到键为 Key
的对象,则返回 true
,否则返回 false
。
指定的 Key
用于通过以下方式识别对象:在 set
表中匹配对象的键,或者在 ordered_set
表中比较等于对象的键(有关差异的详细信息,请参阅 lookup/2
和 new/2
)。
如果指定了默认对象 Default
,则当表中缺少键时,它将用作要更新的对象。忽略键位置的值,并替换为正确的键值。
在以下情况下,函数会因 badarg
而失败
- 表类型不是
set
或ordered_set
。 Pos
< 1.Pos
> 对象元数。- 默认对象的元数小于
<keypos>
。 - 要更新的元素也是键。
此函数返回由TableName
标识的已命名表的tid/0
,如果不存在此类表,则返回undefined
。 tid/0
可以代替表名用于所有操作,这稍微快一些,因为不必在每次调用时都解析名称。
如果表被删除,即使创建了另一个具有相同名称的命名表,tid/0
也将无效。