今天我们来看看ets中两种获取表数据的方法之间的区别
- ets:tab2list(Tab)
- ets:match(Tab,‘_’)
二者都是返回一个 ETS 表的所有对象数据的列表
我们查看源码可以知道tab2list(Tab)底层是直接调用的ets:match_object(Tab, ‘‘)方法,现在我们只需要讨论ets:match(Tab,’’)和ets:match_object(Tab, ‘_’)的效率问题
1. 我们先来看一下代码
-module(auto_insert).
-export([start/1, insert_data/2]).
start(Count) ->
% 创建一个名为my_table的set类型的ets表
Tab = ets:new(my_table, [set, named_table, public]),
% 插入大量数据
insert_data(Tab, Count),
% 测量ets:match/2的效率
{MatchTime, _MatchResult} = timer:tc(fun() -> ets:match(Tab, '_') end),
io:format("ets:match: ~p microseconds~n", [MatchTime]),
% 测量ets:tab2list/1的效率
{ListTime, _ListResult} = timer:tc(fun() -> ets:tab2list(Tab) end),
io:format("ets:tab2list: ~p microseconds~n", [ListTime]).
insert_data(Tab, Count) ->
Data = [ {N, "data" ++ integer_to_list(N)} || N <- lists:seq(1, Count) ],
lists:foreach(fun({Key, Value}) -> ets:insert(Tab, {Key, Value}) end, Data).
我们逐渐递增插入数据库的数据,来看看两者的性能差距是如何
%% 这里可以看出在十万条数据的情况下两者性能只差了1000微秒也就是0.001秒
19> auto_insert:start(100000).
ets:match: 15000 microseconds
ets:tab2list: 16000 microseconds
ok
%%当我把数据提升到一百万时,两者之间的区别就可以明显看出来了,两者之间整整差了两倍还多
20> auto_insert:start(1000000).
ets:match: 94000 microseconds
ets:tab2list: 219000 microseconds
ok
%%当我把数据提升到五百万时,两者之间的区别就可以明显看出来了,两者之间比例正在逐渐拉大
21> auto_insert:start(1000000).
ets:match: 484000 microseconds
ets:tab2list: 1297000 microseconds
ok
%%当我把数据提升到一千万时,两者之间的区别就可以明显看出来了,两者之间差距接近三倍
22> auto_insert:start(10000000).
ets:match: 1078000 microseconds
ets:tab2list: 3031000 microseconds
ok
一千万时需要内存差不多5个G左右,一个亿就不测了大概需要50个G内存,碍于电脑内存瓶颈只有32G,影响测试
测试结果为当数据量越来越大时,两种方法的性能差距趋近于1:3.
为什么两者性能差距如此之大呢?
我们改变一下代码,输出一下两个方法返回的结果就知道了
-module(auto_insert).
-export([start/0, insert_data/2]).
start() ->
% 创建一个名为my_table的set类型的ets表
Tab = ets:new(my_table, [set, named_table, public]),
% 插入大量数据
insert_data(Tab, 5),
% 测量ets:match/2的效率
{MatchTime, _MatchResult} = timer:tc(fun() -> ets:match(Tab, '_') end),
io:format("ets:match: ~p microseconds~n,Result:~p", [MatchTime, _MatchResult]),
% 测量ets:tab2list/1的效率
{ListTime, _ListResult} = timer:tc(fun() -> ets:tab2list(Tab) end),
io:format("ets:tab2list: ~p microseconds~n,Result:~p", [ListTime, _ListResult]).
insert_data(Tab, Count) ->
Data = [{N, "data" ++ integer_to_list(N)} || N <- lists:seq(1, Count)],
lists:foreach(fun({Key, Value}) -> ets:insert(Tab, {Key, Value}) end, Data).
ets:match: 0 microseconds
Result:[[],[],[],[],[]]
ets:tab2list: 0 microseconds
Result:[{1,"data1"},{3,"data3"},{4,"data4"},{2,"data2"},{5,"data5"}]
ets:match(Tab,‘')只返回了空的数组而
ets:match_object(Tab, '’)返回了所有正常数据
这是我们改变一下代码,使用变量’$1’代替’_’
-module(auto_insert).
-export([start/0, insert_data/2]).
start() ->
% 创建一个名为my_table的set类型的ets表
Tab = ets:new(my_table, [set, named_table, public]),
% 插入大量数据
insert_data(Tab, 100000),
% 测量ets:match/2的效率
{MatchTime, _MatchResult} = timer:tc(fun() -> ets:match(Tab, '$1') end),
{ListTime, _ListResult} = timer:tc(fun() -> ets:tab2list(Tab) end),
io:format("Result:~p~n", [_MatchResult]),
io:format("Result:~p~n", [_ListResult]),
io:format("ets:match: ~p microseconds~n", [MatchTime]),
% 测量ets:tab2list/1的效率
io:format("ets:tab2list: ~p microseconds~n", [ListTime]).
insert_data(Tab, Count) ->
Data = [{N, "data" ++ integer_to_list(N)} || N <- lists:seq(1, Count)],
lists:foreach(fun({Key, Value}) -> ets:insert(Tab, {Key, Value}) end, Data).
%%十万个数据时
ets:match: 31000 microseconds
ets:tab2list: 16000 microseconds
%%一百万个数据时
ets:match: 218000 microseconds
ets:tab2list: 235000 microseconds
%%这里一千万内存不足,只测了五百万万个数据时
ets:match: 2719000 microseconds
ets:tab2list: 2672000 microseconds
这是我们发现在十万个数据量的情况下前者是后者的接近两倍,但是在一百万和一千万时是两者性能几乎相同
so
ets:match/2返回一个包含匹配项的列表,每个匹配项是一个元组,其中包含键和值。
ets:match_object/2返回一个包含匹配项的列表,每个匹配项是一个元组,其中只包含值。
因此,ets:match/2需要将每个匹配项的键和值都包含在返回值中,而ets:match_object/2只需要将值包含在返回值中。这可能会导致ets:match/2的性能比ets:match_object/2差。
此外,ets:match/2和ets:match_object/2的性能还取决于ets表的大小和类型。一般来说,ets表越大,性能越差。如果你需要频繁地查找匹配项,可以考虑使用其他数据结构,如Ets的索引或B树。