You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Elixir函数中实现值缓存?覆盖C++静态变量、数据库缓存场景

场景1:计数器函数的等价实现

C++中函数内部的静态变量本质是跨调用持久化的可变状态,Elixir作为无共享可变状态的函数式语言,没有原生支持这类语法,最符合语言设计规范的实现是用Agent进程维护计数器状态:

defmodule Counter do
  use Agent

  # 启动计数器进程,初始值对应C++代码中的static int i = 5
  def start_link(_opts) do
    Agent.start_link(fn -> 5 end, name: __MODULE__)
  end

  def func do
    # 原子性更新状态并返回最新值
    i = Agent.get_and_update(__MODULE__, fn state ->
      new_val = state + 1
      {new_val, new_val}
    end)
    IO.puts("i is #{i}")
  end
end

# 调用测试
Counter.start_link([])
Counter.func() # 输出i is 6
Counter.func() # 输出i is 7

该实现线程安全,状态由独立进程维护,避免了共享可变状态带来的并发问题。

场景2:数据库查询缓存的等价实现

原有代码存在两个问题:一是每次函数调用都会重新初始化空Map作为缓存,二是Elixir的词法作用域规则下,if代码块内部的变量赋值不会影响外部作用域的同名变量。针对缓存需求有两种常用实现方案:

方案1:全局跨进程缓存(ETS实现)

如果需要在多进程间共享缓存,用Erlang内置的ETS表实现性能最优:

defmodule ElixirTesting do
  def start_link(_opts) do
    # 创建全局命名的ETS缓存表,支持并发读
    :ets.new(:db_cache, [:set, :public, :named_table])
    Agent.start_link(fn -> :ok end, name: __MODULE__)
  end

  def test_static_function_variable do
    IO.puts(get_value_from_db_cache("A"))
    IO.puts(get_value_from_db_cache("B"))
    IO.puts(get_value_from_db_cache("A"))
    true
  end

  defp get_value_from_db_cache(key) do
    case :ets.lookup(:db_cache, key) do
      # 命中缓存直接返回
      [{^key, value}] -> value
      # 未命中则查询数据库写入缓存后返回
      [] ->
        value = get_value_directly_from_db(key)
        :ets.insert(:db_cache, {key, value})
        value
    end
  end

  @db_data %{ "A" => 3, "B" => 4, "C" => 5, "D" => 6 }
  defp get_value_directly_from_db(key) do
    IO.puts("Inside get_value_directly_from_db for key #{key}")
    @db_data[key]
  end
end

# 测试调用
ElixirTesting.start_link([])
ElixirTesting.test_static_function_variable()

运行后输出如下,可见第二次查询key"A"时不会触发数据库访问:

Inside get_value_directly_from_db for key A
3
Inside get_value_directly_from_db for key B
4
3

方案2:纯函数实现(无额外依赖)

如果不需要跨调用/跨进程共享缓存,可以将缓存作为参数在函数间传递,完全符合纯函数式编程规范:

defmodule PureElixirTesting do
  def test_static_function_variable do
    cache = %{}
    {val_a, cache} = get_value_from_db_cache("A", cache)
    IO.puts(val_a)
    {val_b, cache} = get_value_from_db_cache("B", cache)
    IO.puts(val_b)
    {val_a2, _} = get_value_from_db_cache("A", cache)
    IO.puts(val_a2)
    true
  end

  defp get_value_from_db_cache(key, cache) do
    if Map.has_key?(cache, key) do
      {cache[key], cache}
    else
      value = get_value_directly_from_db(key)
      {value, Map.put(cache, key, value)}
    end
  end

  @db_data %{ "A" => 3, "B" => 4, "C" => 5, "D" => 6 }
  defp get_value_directly_from_db(key) do
    IO.puts("Inside get_value_directly_from_db for key #{key}")
    @db_data[key]
  end
end

该实现不需要启动额外进程,适合单进程内的批量查询场景。

内容的提问来源于stack exchange,提问作者cppProgrammer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 12:36:05