FaRM — 把一排机器的内存当成一个低延迟仓库
待复核FaRM 是一个把多台服务器的内存连成“共享仓库”的分布式系统。日常类比:你在仓库找零件,不再每次打电话让远处同事帮你拿,而是拿到一张安全通行卡,直接去对应货架读货号。
技术上,这张“通行卡”就是 RDMA:一台机器的网卡可以直接读写另一台机器已经注册好的内存区域,很多时候不用惊动远端 CPU。
FaRM 在这个能力上做了三件事:共享地址空间、严格可串行化事务、单次 RDMA 就能完成的无锁读。它的目标不是只做一个 key-value store,而是给低延迟在线服务提供通用底座。
不理解 FaRM,下面这些事会很难解释:
- 为什么“数据都在内存里”还不够快:跨机器通信仍然会吃掉大部分延迟。
- 为什么 RDMA 系统不只是“换一张快网卡”:页表、连接数、轮询和对象版本都要一起设计。
- 为什么读多写少的服务特别适合一侧 RDMA read:读路径可以绕过远端 CPU。
- 为什么后来的 RDMA 事务系统会反复讨论 one-sided 和 two-sided 的取舍。
-
远端内存像本地货架:FaRM 把对象地址做成 64 位指针,前半段找 region,后半段找 offset。类比:仓库编号加货架坐标,先定位城市,再定位格子。
-
快读和稳写分工:读多的路径用 lock-free RDMA read,写路径仍走事务、复制日志和提交协议。类比:普通顾客自助扫码,改库存必须走柜台登记。
-
性能来自一整套配合:论文的亮点不是单个 RDMA 调用,而是大页注册、连接复用、轮询事件循环、对象版本和数据同机放置。类比:高铁快不只因为车头快,轨道、信号和车站调度也要配套。
案例 1:一次远端读像什么
Section titled “案例 1:一次远端读像什么”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像第二个封条,读完后再确认没有被换。- 版本一致才使用数据,否则重试,避免读到半新半旧的对象。
案例 3:把事务送到数据旁边
Section titled “案例 3:把事务送到数据旁边”machine = owner(order_ptr);msgSend(order_ptr, "add-item", item);// 远端机器本地执行小事务txRead(order);txWrite(order_with_item);txCommit();逐部分解释:
owner找到对象主要存在哪台机器。msgSend把操作发给数据所在机器,而不是把很多对象拉回来。- 本地小事务少发网络消息,常常比跨机器事务便宜。
-
把 RDMA 当成无限快的内存线:远端内存仍比本地内存慢很多,所以 FaRM 还要做对象同机放置。
-
只看 one-sided read 的优雅:写入、复制和恢复仍需要协议,否则快读会读到不一致状态。
-
忽略 NIC 缓存限制:注册太多小页会让网卡频繁取页表,论文里必须用 2GB 大页和 PhyCo 才稳住性能。
-
把 benchmark 数字直接搬到生产:FaRM 的图存储结果基于 LinkBench 和不同硬件,只能说明量级优势,不能等同真实业务 SLA。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 数据工作集能放进集群内存的在线服务。
- 读多写少、对象较小、延迟敏感的 key-value 或图查询。
- 可以为了低延迟接受轮询、绑核、专用网络调优的系统。
不适用:
- 数据主要瓶颈在磁盘或对象很大,网络小包优化帮不上多少的场景。
- 多租户环境里不能长期占用 CPU 轮询的场景。
- 写比例很高、SSD 日志很快成为瓶颈的场景。
- 不愿把应用数据布局和事务边界一起设计的通用业务系统。
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 2011 年前后:RAMCloud 展示了“全部数据进内存,靠快速恢复保可靠”的路线。
- 2013 年:MemC3、TAO、Pilaf 等系统让大家看到内存服务和 RDMA 读路径的潜力。
- 2014 年:FaRM 在 NSDI 发表,把 RDMA、事务和共享地址空间揉成一个完整平台。
- 2016 年之后:FaSST 等后续系统开始反问:one-sided RDMA 很快,但 two-sided RPC 会不会更简单、更可扩展。
-
分布式内存系统的核心矛盾是“本地快、远端慢”:FaRM 用 RDMA 缩小差距,但仍认真优化 locality。
-
一致性不是性能的反面:FaRM 用版本号、incarnation、OCC 和两阶段提交,把 fast path 和 correctness 放在同一个设计里。
-
通用抽象要给逃生门:默认给事务,热点读给 lock-free read,局部复杂操作给 function shipping。
-
硬件能力会改变软件形状:一侧 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 映射。
(暂无反向链接)