共47行

elixir OTP涉及到公用数据是怎么处理的?比如秒杀场景

2026-04-20 18:22:40

Elixir/OTP 处理公用数据(尤其是秒杀类高并发场景)的核心思路是:避开单进程串行瓶颈,利用 BEAM 虚拟机级的原子操作实现无锁并发。针对“秒杀”这种极端场景,ETS 原子计数器是性能最优解。

秒杀场景的 ETS 原子计数方案

这是 Discord 等超大规模应用验证过的方案,性能远超单 GenServer。

核心机制

代码骨架

defmodule SpikeSale do
  # 启动时创建 ETS 表(通常在 Supervision 树中)
  def start_link do
    :ets.new(:inventory, [:named_table, :set, :public, write_concurrency: true])
    :ets.insert(:inventory, {:iphone, 100}) # 初始库存
  end

  # 秒杀扣减逻辑(直接由 Web 请求进程调用)
  def grab(product) do
    # 原子性:查询并递减,返回更新后的值
    case :ets.update_counter(:inventory, product, -1, {:iphone, 100}) do
      new_stock when new_stock >= 0 -> {:ok, new_stock}
      _ -> :out_of_stock
    end
  end
end

其他公用数据方案的适用边界

秒杀场景(高频写+强一致性)是特例,其他场景需根据读写模式选型:

方案 适用场景 秒杀场景痛点
GenServer / Agent 低频写、需复杂业务逻辑、顺序执行 单进程串行,消息队列易积压,成为性能瓶颈
ETS + GenServer 高频读、低频写(如缓存) 写操作若经 GenServer 路由,仍存在单点瓶颈
Registry 进程发现、动态命名(如用户会话管理) 不直接存储业务状态,通常配合其他方案使用

进阶优化与注意事项

决策建议:电商项目中,若遇到真正的秒杀场景(QPS > 1万),请直接使用 ETS 原子计数器;若只是普通抢购(QPS < 1000),使用 GenServer 保持代码简单即可。