跳转到内容

FaRM — 把一排机器的内存当成一个低延迟仓库

待复核

FaRM 是一个把多台服务器的内存连成“共享仓库”的分布式系统。日常类比:你在仓库找零件,不再每次打电话让远处同事帮你拿,而是拿到一张安全通行卡,直接去对应货架读货号。

技术上,这张“通行卡”就是 RDMA:一台机器的网卡可以直接读写另一台机器已经注册好的内存区域,很多时候不用惊动远端 CPU。

FaRM 在这个能力上做了三件事:共享地址空间、严格可串行化事务、单次 RDMA 就能完成的无锁读。它的目标不是只做一个 key-value store,而是给低延迟在线服务提供通用底座。

不理解 FaRM,下面这些事会很难解释:

  • 为什么“数据都在内存里”还不够快:跨机器通信仍然会吃掉大部分延迟。
  • 为什么 RDMA 系统不只是“换一张快网卡”:页表、连接数、轮询和对象版本都要一起设计。
  • 为什么读多写少的服务特别适合一侧 RDMA read:读路径可以绕过远端 CPU。
  • 为什么后来的 RDMA 事务系统会反复讨论 one-sided 和 two-sided 的取舍。
  1. 远端内存像本地货架:FaRM 把对象地址做成 64 位指针,前半段找 region,后半段找 offset。类比:仓库编号加货架坐标,先定位城市,再定位格子。

  2. 快读和稳写分工:读多的路径用 lock-free RDMA read,写路径仍走事务、复制日志和提交协议。类比:普通顾客自助扫码,改库存必须走柜台登记。

  3. 性能来自一整套配合:论文的亮点不是单个 RDMA 调用,而是大页注册、连接复用、轮询事件循环、对象版本和数据同机放置。类比:高铁快不只因为车头快,轨道、信号和车站调度也要配套。

Addr a = locate("user:42");
Obj *o = rdma_read(a, sizeof(User));
if (version_ok(o)) {
return o->name;
}
retry();

逐部分解释

  • locate 找到对象在共享地址空间里的位置。
  • rdma_read 让本机网卡直接去远端内存拿数据。
  • version_ok 检查读到的对象有没有撞上并发写。

案例 2:为什么对象里要放版本号

Section titled “案例 2:为什么对象里要放版本号”
before = obj->version;
payload = obj->data;
after = obj->version_copy;
if (before == after && unlocked(before)) {
use(payload);
}

逐部分解释

  • before 像货架封条,读之前先看一次。
  • after 像第二个封条,读完后再确认没有被换。
  • 版本一致才使用数据,否则重试,避免读到半新半旧的对象。
machine = owner(order_ptr);
msgSend(order_ptr, "add-item", item);
// 远端机器本地执行小事务
txRead(order);
txWrite(order_with_item);
txCommit();

逐部分解释

  • owner 找到对象主要存在哪台机器。
  • msgSend 把操作发给数据所在机器,而不是把很多对象拉回来。
  • 本地小事务少发网络消息,常常比跨机器事务便宜。
  1. 把 RDMA 当成无限快的内存线:远端内存仍比本地内存慢很多,所以 FaRM 还要做对象同机放置。

  2. 只看 one-sided read 的优雅:写入、复制和恢复仍需要协议,否则快读会读到不一致状态。

  3. 忽略 NIC 缓存限制:注册太多小页会让网卡频繁取页表,论文里必须用 2GB 大页和 PhyCo 才稳住性能。

  4. 把 benchmark 数字直接搬到生产:FaRM 的图存储结果基于 LinkBench 和不同硬件,只能说明量级优势,不能等同真实业务 SLA。

适用

  • 数据工作集能放进集群内存的在线服务。
  • 读多写少、对象较小、延迟敏感的 key-value 或图查询。
  • 可以为了低延迟接受轮询、绑核、专用网络调优的系统。

不适用

  • 数据主要瓶颈在磁盘或对象很大,网络小包优化帮不上多少的场景。
  • 多租户环境里不能长期占用 CPU 轮询的场景。
  • 写比例很高、SSD 日志很快成为瓶颈的场景。
  • 不愿把应用数据布局和事务边界一起设计的通用业务系统。
  • 2011 年前后:RAMCloud 展示了“全部数据进内存,靠快速恢复保可靠”的路线。
  • 2013 年:MemC3、TAO、Pilaf 等系统让大家看到内存服务和 RDMA 读路径的潜力。
  • 2014 年:FaRM 在 NSDI 发表,把 RDMA、事务和共享地址空间揉成一个完整平台。
  • 2016 年之后:FaSST 等后续系统开始反问:one-sided RDMA 很快,但 two-sided RPC 会不会更简单、更可扩展。
  1. 分布式内存系统的核心矛盾是“本地快、远端慢”:FaRM 用 RDMA 缩小差距,但仍认真优化 locality。

  2. 一致性不是性能的反面:FaRM 用版本号、incarnation、OCC 和两阶段提交,把 fast path 和 correctness 放在同一个设计里。

  3. 通用抽象要给逃生门:默认给事务,热点读给 lock-free read,局部复杂操作给 function shipping。

  4. 硬件能力会改变软件形状:一侧 RDMA read 让“远端 CPU 不参与读”成为设计目标,也暴露出 NIC 缓存和连接状态的新瓶颈。

  • 论文 PDF:FaRM: Fast Remote Memory(NSDI 2014,本文主来源)
  • tao-2013 —— FaRM 的图存储实验复刻了 TAO 风格 workload,适合对照读。
  • memcached-fb-2013 —— 读多的小对象缓存场景,是 FaRM key-value 设计的现实背景。
  • sinfonia-2007 —— 同样提供共享地址空间和事务,能看出 FaRM 为什么要借 RDMA 降低成本。
  • fasst-2016 —— 后续 RDMA 事务系统,反过来质疑 one-sided 设计的复杂度。
  • gpudirect-rdma-2014 —— 另一个“让设备绕过 CPU 直接搬数据”的 RDMA 应用方向。
  • tao-2013 —— FaRM 用 TAO 风格图查询证明低延迟图存储可行。
  • memcached-fb-2013 —— FaRM 的 key-value 场景继承了大规模缓存服务的读多特点。
  • sinfonia-2007 —— 都给程序员共享地址空间和事务,FaRM 把硬件 fast path 加了进去。
  • gray-1981-transaction —— FaRM 的严格事务语义仍站在传统事务理论上。
  • spanner-2012 —— 都追求强一致,只是 Spanner 靠时间,FaRM 靠 RDMA 和对象版本。
  • zookeeper —— FaRM 用类似集群成员管理来维护机器集合和 region 映射。

(暂无反向链接)