OPED 是 (我自己发起的) One Paper Each Day 挑战, 即每天读一篇 Paper, 领域不限.

这是 OPED 挑战的第六篇, 论文为 Fast key-value stores: An idea whose time has come and gone


说好的 OPED, 结果停更了这么久. 因为最近实在是太忙()了, 工作上有一个大的上线, 然后最近期待很久的游戏也发售了. 工作日早起打游戏, 我还是肝得动.


这篇论文看标题有一点朋友圈标题党的感觉. 它的观点确实有点让人耳目一新. 但我的感觉是, 道理有的, 但是对于一般技术团队来说, 操作性不高, 性价比比较差, 可以暂时观望一下.

这篇论文的核心观点是, RInK (即 Remote in-memory KV Store) 虽然能帮助我们开发 stateless 的服务, 但是它们都有一些无法避免的天然缺陷. 开发者们应该使用 LInK (即 Local in-memory KV Store) 来开发 stateful 的服务.

上面的声明是不是让你觉得不可思议? 什么? 无状态的服务多么好管理, 多么容易水平扩展? Stateful? 开历史的倒车!

Silence

先静一静, 让我们先听他解释.

RInK, 包括 Redis, Memcached 等等, 作为一个通用的内存存储服务, 往往提供了通用的接口, 比如 GET/PUT (当然有一部分会提供一些高级一点的接口, 比如 append 之类的). 与此同时, 这些服务都需要通过 RPC 调用来使用. 以上这两个特点会带来三个无法避免的问题

  1. 序列化/反序列化开销. 各种编程语言中的数据在内存中的结构, 往往和 RInK 中存储的结构不同. 因此需要序列化和反序列化. 说出来你可能不信, 在很多服务中, 大部分 CPU 资源都是在干这个 (因为业务逻辑除了调用外部服务, 其他的都比较简单…)
  2. 为了保证通用性, 接口往往只能返回 key 对应的整个 value, 即使调用方只需要其中的一点点数据. 用于数据量大, 带宽压力, 反序列化压力也随之增大. 这也被称作 overreads 问题.
  3. 最后一个显而易见的问题当然是网络延迟. 确实大部分 RInK 服务的延迟都很低. 所以, 大部分情况下, 只有 tail latency 会受到影响. 但是如果你看了之前的文章, 再想想你的服务中, 处理一个请求, 要调用多少次 redis, 你可能会觉得事情并没有想象的那么简单.

说了那么多, 到底怎么解决这些问题呢? 这篇论文说他们用库的形式提供了 KV 服务, 还提供 replication, sharding, routing 等等功能. 然而关于实现细节一点没有讲… 这篇 7 页的论文着实有点让人失望.

总结一下, 我个人觉得性能上的损耗相对于更高的可维护性来说, 是一个可以承受的 trade off. LInK 确实有一定的好处, 但是在有成熟的实现之前, 还是先用一下 RInK 吧. 关于 overreads, 这就体现出 GraphQL 的优越性了.