Codis 的设计与实现 Part 3

c4pt0r · · 3340 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

性能,HA (高可用),运维等等

「对于一个设计良好的分布式系统来说,单机性能从来不是一个问题 」—— 我

第一篇文章就说了,Codis 采用了 Proxy 的方案,所以必然会带来单机性能的损失,经测试,在不开 pipeline 的情况下,大概会损失 40% 左右的性能,但是 Redis 本身是一个快得吓人的东西,即使单机损失了 40% 仍然是一个很大的数字。
另外一个比较好的地方是,Codis 本身是可以充分的利用多核的(Thanks to golang),在多线程客户端的环境下,不会像 Twemproxy 那样,撑死就跑满一个 CPU(当然你可以部署 Twemproxy 的多实例,但不就又增加运维成本了嘛。。。)。而且不要忘了,
Codis 可以通过平行扩展多个 Proxy 实现性能成倍的提升,一台机器 CPU 满了?没事,再另一台机器起一个 proxy 就是了。某个 Redis 跑满了?没事,把一部分数据迁移到另外一个 Redis 实例好了。所以,我们从来不认为单机性能能说明什么。 使用 Codis 带来的最大的好处,是为你的缓存提供了弹性扩缩容的能力, 而不是追求榨干底下 Redis 的性能。。。这也是我们选择了 go 而不是 c 来开发的原因。

我们进行了一个详细的性能测试,测试的结果如下:Benchmark , 2 proxy 的情况下,单机可以到达 20w 的 QPS.

HA (Redis HA, Proxy HA)

对于Redis HA, 我个人是蛮纠结的,仔细用过的 Codis 的同学会发现,在某个 server group 的 master 死掉的时候,虽然 server group 里可以有多个 slave,但是这些 slave 并不会自动的提升为 master。
当然实现这个功能并不困难,但是我认为这种情况应该让管理员清楚,并手动的操作,因为如果自动的切到 slave 上,这段时间原 master 还没同步到 slave 上的数据有可能就会冲突,如果 master 又复活了,解决数据的冲突是一个麻烦的问题,与其自动的操作,不如给客户端返回失败 (也只是这个机器负责的slots 会失败,如果实例足够多,不会出现致命的单点故障), 交给管理员去处理。

Proxy HA, 在决定使用无状态的 proxy 的方案时,就自动带来了高可用性的保证,这个不多说,有很多种做法,比如智能DNS,HAProxy,客户端连接 ZooKeeper 做 Proxy 的连接池等。

说到可运维性,Codis 几乎一切的操作都是通过 codis-config 发起的,codis-config 在做任何操作的时候,都会到 ZooKeeper 拿一个锁,以保证是唯一的操作实例,这也是防止路由表被改坏的一个措施,尤其是设置迁移这样比较敏感的操作,必须保证不能同时有多个 slots 处于迁移状态,所以,在整个迁移的过程中,是不释放锁的。

那么如果在迁移一个 slot 的过程中,我强行杀死 (kill -9) codis-config 不让它释放迁移的锁会怎么样?会死锁吗?

答案是:不会的。

为什么?首先,在迁移过程中的任意阶段打断,都是没有问题的,因为对底层的 redis 来说,迁移的只是一个个原子的 key,我杀掉了 codis-config ,只是停止了发指令的人,导致这个 slot 没有全部迁移干净而已, 在 Zk 上看到的也只是一个长期处于迁移状态的 slot (由于 codis-config 被杀了,没人去发迁移命令,也不会在迁移完成的时候修改slot状态了). 此时如果客户端有请求,proxy 也会主动的发一个 migratekey 先强行的把这个key迁移过来,所以对客户端也是没有影响的。

而且 codis-config 还有一个特性,每次启动的时候,都会在 Zk 上注册一个临时节点, 记录自己的 pid 和机器名,而且上的所有的锁,都会带上机器名和pid的签名,每次启动的时候,会扫描一下所有未释放的锁,如果这个锁的所属进程的临时节点已经不存在了,就会直接把这个锁释放掉,于是避免了死锁的状态。

只不过在下次开始新的迁移任务之前,需要先将这个未迁移完成的slot迁移掉,方法是发起一个迁移任务,from slot 和 to slot 都写成这个slot id, new group id设成之前未完成的那个任务的group id, 这个是为了保证系统能重新回到一个干净的状态,再进行下一个新的迁移任务。这是一个人为加的限制。

在实际使用 Codis 的过程中,我们为一些特别懒的业务 (懒得重建缓存),开发了叫做redis-port的工具,它的作用是,作为一个假的 slave,挂在一个redis后面,然后将master的数据同步回来,sync 到 codis 集群上,所以,业务方根本无需重建缓存,直接同步完后,换个地址重启服务就OK了, 这也是问哦们在公司内部推动项目特别轻松的一个杀手锏。 :P

除此之外,Codis 还有一个和其他后端中间件不太一样的地方:它不仅提供了完整的 Unix CLI interface, 居然有一个炫酷的 dashboard 和完整的 RESTful API ! 恩,没错,而且在实际的生产环境中,我们发现使用 dashboard 更安全,更少误操作,更方便,而且系统的实时状态都非常清晰。这年头,要没有一个好看的 dashboard 都不好意思开源了。。。(嘛,其实是我刚学 AngularJS,技痒了。。。不过最终效果还不错)。

三篇文章差不多写完了,有什么问题可以在 github 上 post issue, 也可以在微博上联系我 @Dongxu_Huang@goroutine, 当然,邮件也可以,同时如果你对豌豆荚基础架构组有兴趣,也欢迎你把简历发过来,我们是 Golang 的重度用户 :) 也是开源的狂热分子,有机会希望和你一起工作, email: huangdongxu1987@gmail.com

announce一下 Codis 的作者们:

@Dongxu_huang (aka c4pt0r): codis-config, 分布式协议实现,数据迁移及auto rebalancer,Dashboard, unit-test…
@goroutine (aka ngaut): codis-proxy, Redis 协议解析, router,zkhelper, unit-test…
@spinlock9: redis patch, redis-port, benchmark, test …


有疑问加站长微信联系(非本文作者)

本文来自:dongxu.h

感谢作者:c4pt0r

查看原文:Codis 的设计与实现 Part 3

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3340 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传