最近开源了 Codis,在 Github 上的反响挺不错的,3天已经收集到了 1000 多 stars, 让我比较吃惊。 也从侧面说明了确实分布式缓存是大家都会遇到的问题。于是我打算在本篇和接下来的几篇 Blog 详细说明一下 Codis 的设计和一些背后的考虑,以及对于分布式存储(尤其是缓存)系统的一些思考。
Why proxy?
Codis 的架构采用了 Proxy-based 的设计,没有走官方 Cluster 的路,官方的 Cluster 实现是 P2P 的模型,依靠 Gossip 协议进行消息同步和将数据分若干个 Slot 作为管理的单位,客户端需要更改。这个模型的好处是:
- 真正的无中心节点
- 对于客户端来说请求的性能不会损失太多
但是缺点同样明显:
- 状态很难明确,你很难清楚的知道集群现在处于什么状态
- 对与redis来说,集群的升级困难,运维困难,因为它将分布式的逻辑和存储引擎绑定在了一起。
- 需要依赖 smart client
这两个缺点几乎在任何 p2p 模型的分布式系统中都存在,由于第一个问题,导致开发和调试的过程也异常艰辛(官方的 cluster 几乎写了 3 年才比较稳定)
而 Proxy-based 的方式的好处就比较明显了:
- 开发成本低
- 业务的切换成本低
- Proxy 的逻辑和存储的逻辑是隔离的
所以,在 Codis 之前,Twemproxy 是这个方案的最优的选择,应用也非常广泛,许多大型的互联网公司都在使用它,但是 Twemproxy 也有它的问题, 最大的问题是:Twemproxy 真的就只是一个 Proxy 而已, 集群的功能完全没有。而且看上去 Twitter 也不打算继续维护它了。
Twitter 最近的一个 Talk,提到了Twitter 内部对于 Scaling Redis 的一些做法和想法,其中对于 Proxy 的方案是比较推崇的(同时也提到了他们内部也不再使用 Twemproxy 了….),里面理由写得比较清楚了,有兴趣的可以去看看。同样的, Facebook 之前那篇关于扩展 memcached 的论文也提到了类似的方案 (mcrouter)。其中我觉得这个方案背后最重要的思想就是:存储和分布式逻辑分离。至于因为转发请求而损失的性能,可以通过其他的方式补回来, 比如水平扩展 Proxy。带来的好处是整个系统的状态非常清晰,几乎所有组件都可以独立的部署和升级,程序还比较好写,所以 Codis 从一开始就坚定的走 Proxy 这条路。
但是相对于 Twemproxy, Codis 又有一些改进,首先集成了集群的功能,使用 Presharding 对分散数据的存储。所有的集群状态信息依赖 ZooKeeper 进行同步, 所有的 Proxy 是无状态的。这样就可以实现多 Proxy 的水平扩展。
另外一个比较重要的决定是使用 Go 作为主要开发语言,抛掉信仰问题不谈 (我和 @goroutine 都是 go 的脑残粉), go 几乎就是针对这种后端的网络程序而发明出来的语言,这给开发工作带来了极大的效率提升,从写下第一行代码,到第一个可用版本,几乎才用了不到一个月的时间。
当然,使用多 Proxy 对于迁移过程中数据一致性带来一些问题,会在下一篇 blog 介绍一下 Codis 是怎么解决的。
有疑问加站长微信联系(非本文作者)