database - 哪种 Erlang 数据结构可用于有序集并且可以进行查找?

标签 database data-structures erlang set

我正在解决一个问题,我需要记住收到的事件的顺序,但也需要根据事件的 ID 查找事件。如果没有第三方库,我怎样才能在 Erlang 中有效地做到这一点?请注意,我有许多潜在的短暂 Actor ,每个 Actor 都有自己的事件(已经被认为是健忘症,但它需要表格的原子,并且如果我的 Actor 死了,表格就会保留下来)。

-record(event, {id, timestamp, type, data}).

最佳答案

根据 Michael 答案评论中的讨论中包含的详细信息,一个非常简单、可行的方法是在流程状态变量中创建一个元组,该元组将事件的顺序与事件的顺序分开存储。事件的 K-V 存储。

考虑:

%%% Some type definitions so we know exactly what we're dealing with.
-type id()     :: term().
-type type()   :: atom().
-type data()   :: term().
-type ts()     :: calendar:datetime().
-type event()  :: {id(), ts(), type(), data()}.
-type events() :: dict:dict(id(), {type(), data(), ts()}).

% State record for the process.
% Should include whatever else the process deals with.
-record(s,
        {log    :: [id()],
         events :: event_store()}).

%%% Interface functions we will expose over this module.
-spec lookup(pid(), id()) -> {ok, event()} | error.
lookup(Pid, ID) ->
    gen_server:call(Pid, {lookup, ID}).

-spec latest(pid()) -> {ok, event()} | error.
latest(Pid) ->
    gen_server:call(Pid, get_latest).

-spec notify(pid(), event()) -> ok.
notify(Pid, Event) ->
    gen_server:cast(Pid, {new, Event}).

%%% gen_server handlers
handle_call({lookup, ID}, State#s{events = Events}) ->
    Result = find(ID, Events),
    {reply, Result, State};
handle_call(get_latest, State#s{log = [Last | _], events = Events}) ->
    Result = find(Last, Events),
    {reply, Result, State};
% ... and so on...

handle_cast({new, Event}, State) ->
    {ok, NewState} = catalog(Event, State),
    {noreply, NewState};
% ...

%%% Implementation functions
find(ID, Events) ->
    case dict:find(ID, Events) of
        {Type, Data, Timestamp} -> {ok, {ID, Timestamp, Type, Data}};
        Error                   -> Error
    end.

catalog({ID, Timestamp, Type, Data},
        State#s{log = Log, events = Events}) ->
    NewEvents = dict:store(ID, {Type, Data, Timestamp}, Events),
    NewLog = [ID | Log],
    {ok, State#s{log = NewLog, events = NewEvents}}.

这是一个完全简单的实现,并将数据结构的细节隐藏在流程接口(interface)后面。我为什么选择字典?只是因为(这很容易)。如果不更好地了解您的需求,我真的没有理由在 gb_tree 等映射上选择字典。如果您有相对较小的数据(要存储数百或数千个内容),那么这些结构之间的性能通常不会有明显差异。

重要的是,您要清楚地确定该流程应响应哪些消息,然后通过创建公开函数的接口(interface)来强制自己在项目代码的其他地方坚持它 em> 在此模块上。在这后面你可以把字典换成其他东西。如果您确实只需要最新的事件 ID 并且不需要从序列日志中提取第 N 个事件,那么您可以放弃日志,只将最后一个事件的 ID 保留在记录而不是列表中。

因此,首先让像这样非常简单的东西起作用,然后确定它是否确实适合您的需要。如果没有,请调整它。如果现在有效,那就运行它吧——不要过分关注性能或存储(除非你真的被迫这么做)。

如果您稍后发现存在性能问题,请将字典和列表切换为其他内容 - 也许是 gb_tree 或 orddict 或 ETS 或其他内容。关键是现在就让某些东西发挥作用,以便您有一个基础来评估功能并在必要时运行基准测试。 (不过,大多数情况下,我发现无论我开始时指定的原型(prototype)是什么,结果都非常接近最终的解决方案。 )

关于database - 哪种 Erlang 数据结构可用于有序集并且可以进行查找?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34598180/

相关文章:

data-structures - 哪个数据结构用于 "dynamic"优先级排队?

java - 用于查询处理和数学运算的表状数据结构

erlang - 如何仅从/向 mnesia 备份/恢复单个表?

Erlang: "include_lib"和 "include"有什么区别?

binary - 将二进制中的以 2 为基数的数字转换为 Erlang 整数

c# - 如何获取XML中带有列名的数据?

c# - 变量未声明或从未分配?和数据读取器不工作?

mysql - 从用户 ID 不在(另一个表)的表中选择 * 作为 ALLIDS,同时还计算它们在另一个表中出现的次数

javascript - 第一次更新时对填充字段进行 Sequelize

PHP 二叉树实现