如何从 Erlang/Elixir 中的 ets 集中选择一个随机元素?

Posted

技术标签:

【中文标题】如何从 Erlang/Elixir 中的 ets 集中选择一个随机元素?【英文标题】:How do I select a random element from an ets set in Erlang/Elixir? 【发布时间】:2019-05-11 15:26:06 【问题描述】:

我需要在 ets 集中跟踪大量进程,然后随机选择单个进程。所以我创建了这样的集合:

:ets.new(:pid_lookup, [:set, :protected, :named_table])

那么为了争论,让我们在其中粘贴self() 1000 次:

Enum.map 1..1000, fn x -> :ets.insert(:pid_lookup, x, self()) end

现在我需要随机选择一个。我知道我可以使用:ets.lookup(:pid_lookup, :rand.uniform(1000)) 随机选择一个,但是如果我事先不知道集合的大小(在上述情况下为 1000)怎么办?

如何找出 ets 集的大小?和/或是否有更好的方法从 ets 数据结构中选择随机 pid?

【问题讨论】:

您可以使用:ets.info(tab, :size) 来获取表格的大小。不过,我真的不知道从中获取随机元素的最佳选择。从集合中获取随机 pid 的用例是什么? @JustinWood 我只是对 genservers 进行基准测试。我正在创建其中的 1000 个,然后我希望每个都向另一个随机发送消息多次,并查看使用各种消息大小需要多长时间。但是当我创建 Genservers 时,它们会启动,因此其他 genservers 在它们开始发送时可能还没有启动。所以我需要知道在基准测试的初始阶段创建了多少。 嗯,1000 当然是任意的。我将根据各种数字检查性能。我本质上是想了解消息传递的速度。 如果你只是想测试消息传递,为什么不启动所有的 gen 服务器然后开始发送消息? 嗯,我确实想知道如何在 ets 表中随机选择一些东西来进行我所做的一堆统计工作。但是要直接回答您的问题,当我启动 1000 个 Genserver 时,第一个将在最后一个启动之前开始发送。因此,我不能只从 1000 个中选择一个随机数。我需要知道已经旋转了多少,为此我需要知道 ETS 表的大小。 【参考方案1】: 如果键是序号
tab = :ets.new(:tab, [])
Enum.each(1..1000, & :ets.insert(tab, &1, :value))
size = :ets.info(tab,  :size) 
# size = 1000
value_picked_randomly = :ets.lookup(tab, Enum.random(1..1000)) 

:ets.info(tab, :size) 返回表的大小;这是在给定表上插入的许多记录。

如果您不知道密钥是
first = :ets.first(tab)
:ets.lookup(tab, first)
func = fn key->
    if function_that_may_return_true() do
        key = case :ets.next(tab, key) do
         :'$end_of_table' -> throw :reached_end_of_table
         key -> func.(key)
        end
    else 
        :ets.lookup(tab, key)
    end
end
func.()

func 将遍历 ets 表并返回一个随机值。 这将非常耗时,因此对于具有大量记录的表来说,这不是一个理想的解决方案。

【讨论】:

【参考方案2】:

据我从 cmets 了解到,这是一个 XY Problem。

您本质上需要的是追踪不断变化的列表并随机选取其中一个元素。一般的ETS 和特别的:ets.set 绝不是为了查询大小。它们有不同的用途。

在您的监督树中生成一个 Agent,保存已启动服务器的 PID 列表,并使用 Kernel.length/1 查询其大小,如果列表不正确,甚至使用 Enum.random/1 > 巨大(后者遍历整个可枚举得到一个随机元素。)

【讨论】:

只有当您不担心对您的代理进行 DDOS 攻击时,这才是一个解决方案。我特别避免了涉及单进程瓶颈的解决方案。 1000 只是一个例子。 cmets 进一步声明我正在进行基准测试。因此,可以合理地假设要跟踪的数量可能会变大几个数量级。因此,ETS。这个问题真的很清楚。我没有要求别人对我的架构进行第二次猜测,我只是询问了有关 ETS 的具体问题,即如何找到大小或如何随机抽样。 我 102% 肯定 Agent 在这种情况下会做得很好,但如果你坚持使用 ETS,只需跟踪数字就是单独的 ETS。插入 PIDs ETS 后,还要增加计数器。

以上是关于如何从 Erlang/Elixir 中的 ets 集中选择一个随机元素?的主要内容,如果未能解决你的问题,请参考以下文章

Erlang 二进制泄漏?

Erlang - Elixir:啥是监督树?

Elixir/Erlang 中的命名函数是不是有等效于 __MODULE__ 的方法?

如何在服务器上正确安装 Erlang、Elixir 和 mix?

Erlang/Elixir精选-第2期(20191209)

Docker 上的 Erlang/Elixir 和热代码交换