codis,codis3.0和其他
如不少人所看到的,Codis 发布了3.0的alpha版 。这个版本本来叫2.1的,但是因为改动比较大,直接改名叫3.0了。Codis发布至今一年有余,我参与Codis项目也有大半年,在这个继往开来的好日子里,有必要讲讲很多想法——只代表我个人的想法。
Codis是Redis的集群解决方案,这意味着他基于Redis,继承了Redis的部分优点,也继承了部分缺点。换句话说,让Redis得到了水平扩展的能力,但依旧无法突破Redis自身的诸多局限性。以及在Github中的提问中发现,其实不少人并没有严格区分Redis和Codis,会觉得这是一种东西,所以很多时候问的问题其实是Redis的问题。
在Codis之前、之后,除了在client自行实现分片逻辑之外,无外乎还有两种方案,twemproxy和官方的redis cluster,我在《 离职系列文章之redis cluster使用经验 》中提到过redis cluster的一些问题,不过现在回头来看并没有完全解释清楚。前不久在Codis的 README 中我添加了一个表格,如下:
Codis | Twemproxy | Redis Cluster | |
---|---|---|---|
resharding without restarting cluster | Yes | No | Yes |
pipeline | Yes | Yes | No |
hash tags for multi-key operations | Yes | Yes | Yes |
multi-key operations while resharding | Yes | – | No( details ) |
Redis clients supporting | Any clients | Any clients | Clients have to support cluster protocol |
这个对比就比较容易看出,Codis和Twemproxy是一类,限制更少,但是Twemproxy不支持动态增减节点,而Codis和Twemproxy的主要区别就是加了这个功能——也就是迁移数据。Codis的迁移的核心就是一个二阶段提交,因而从分布式系统的角度上讲,或者说对于懂分布式系统的人来说,Codis不复杂,更不“牛逼”。但他就是解决了人们的需求,同时又开源(估计不少公司会有类似的内部轮子),所以火了起来。
说实话,很多搞分布式系统的人,很容易觉得别人做的东西不牛逼(没技术含量),也很容易觉得自己做的东西不牛逼(俗称天天打杂),因为牛逼的事情确实不多,更多的是边边角角的杂事,但是不做又不行,或者说不做就丢饭碗了。一个牛逼的东西从把需求转化成系统设计(或者抄现成设计)到写代码再到根据线上情况各种优化/bugfix,看上去只有第一步是牛逼的,但是实际上实现的过程是最复杂也最需要堆人力的,后续优化也很苦,可能跟一个bug跟了几天最后改代码就几行(换成个不太懂技术的老板直接就无法衡量工作量了是不是)。而且同样的设计不同的人实现结果也可能有很大不同,比如把GFS实现成HDFS就各种屎……所以作为一个比较实用主义、既无洁癖也无各种邪教心思的人,我更看重一个东西的利弊是如何取舍的、一个东西的利是不是我想要的、一个东西的弊是不是我不那么在乎的。
那目前的Codis(2.0)有啥弊端呢?dashboard与proxy的通信通过zk,导致一些小公司不得不单独为了Codis而搭一套zk,也导致一旦与zk的连接出了问题,整个Codis会非常脆弱。后者经常会导致Proxy挂掉,虽然可以通过各种优化细节防止他挂掉又避免导致数据出问题,但是最终还是显得比较绕。于是@斯宾洛克 重新设计和实现了这套逻辑,直接让dashboard和proxy互相通过http协议通信,不需要zk了——当然依然允许proxy注册在zk上从而实现服务发现的功能,动态增加新的可用的proxy、剔除不响应的proxy。
于是Codis3.0应运而生。
简单的一句“互相通过http协议通信,不需要zk了”,自然是需要很多工作量的。而且之前zk除了负责通信之外还负责持久化存储集群的节点信息,这个无论怎么改永远是要有的。因此3.0需要在配置中指定如何做持久化的存储,理论上只要实现了对应的CRUD接口就可以存储在任何地方,比如zk、mysql等等。这样哪怕依然把集群状态数据存储在zk,对zk的依赖也减轻了很多。当然,因为zk一般至少三台所以相当于互相备份并且高可用,如果存储在mysql就要自行考虑可用性和备份的问题了,否则mysql成了单点的话,mysql挂了甚至数据丢了对Codis会造成比较大的影响。
而“很多工作量”意味着其逻辑比之前复杂,潜在的bug可能也比较多,因而alpha里正式版发布还有一点距离。当然,一般来说正式版发布只意味着作者认为它是生产环境可用,不代表真的一定可用……现在无论数据库还是编程语言,用户的测试总是会比团队内部的测试发现更多的问题,而用户是基本上不会主动地用beta/rc来帮忙测试的,只会等真靠谱的了再升级。所以一个大版本的第一个正式小版本基本上都会有各种bug,因此大多数人都在等第二个小版本,比如x.y.1,这样x.y.0的问题可能就更难发现,从而x.y.1改了一些bug还会有另一些bug,最终导致恶性循环,大家先等.1,过几个大版本发现不靠谱就都等.2……Go现在就是类似的情况,Cassandra之前是这个情况而且大家是直接去等.5……为了避免这种情况Cassandra现在引入了个人觉得更坑爹的tick-tock机制……Codis2.0的第一个正式版是2.0.2,目前也更新到了2.0.11,其中有不少版本对于一些特定场合是比较严重的bug,也说明我们很难保证在一开始就十分完美。总之,3.0还有一些工作要做,也很难定义一个发布的时间表。并且即使3.0发布正式版之后,可能也不会马上取代目前的master分支(对应2.0版本)作为用户的默认选择,2.0也不会很快EOL因为3.0的改动比较大导致一些用户可能不会很快升级。
那3.0之后呢?目前来说还没看到明确的改进方向。总体来说应该会进入一种比较稳定的状态——回复问题、改bug,不加新feature。Codis作为豌豆荚内部的服务,开源之后各种用户累计在生产环境搭的集群数量远超豌豆荚内部搭的数量。但维护Codis的人毕竟是拿豌豆荚的工资的,在Codis比较成熟之后依然拿着公司的工资全职搞Codis的话对公司是一种浪费(毕竟俺们是创业公司),对个人也是一种摧残(所谓的进入打杂阶段),更可能导致因为时间充裕为了改进而改进去做各种伪需求。最近几个月实际上Codis的开发人力只有“1.1个人”,斯宾洛克把全部精力投入3.0的设计与实现,而我做公司内的其他项目用少部分工作+业余时间(我凌晨回复issue是因为我在家睡得晚,不是因为加班……)回复用户的问题、改可能出现的2.0的bug。等3.0彻底搞完估计会从1.1降格到0.2-0.3左右,除非我和他都跑路了不然Codis是一定会一直维护的。当然假设以后真跑路了而且内部没有其他人接手的话,遇到bug我们也不会见死不救的……
当然在此有必要强调一下,希望各位理解: Github的issue 是Codis官方唯一指定的讨论问题的场所。建议所有用户仔细阅读文档( Doc目录的全部内容 )后再提问,并适当搜索老的问题,以节省大家的时间。在其他社区、社交网站提问,我们无法保证第三方的回答是正确、及时、有时效性的。之所以没有开设任何国内部分程序员很喜欢的QQ群主要是因为QQ群并不是一个很好的讨论问题的场所,要么容易出现日经月经重复问题,要么容易水。此外不接受任何微博私信/email点对点提问,因为这些内容无法被搜索引擎收入也无法让其他人看到,更容易导致每天见到重复的问题以降低我们的效率。理论上似乎Codis可以试水类似“付费VIP服务”,也就是“企业版”,不过这事情没那么简单……
最后,长期来看我依然觉得Redis并不是一个“好的数据库”,因为他的数据容量只取决于内存大小,持久化功能有一定局限性,同时又是单线程。并且我也依然觉得,内存数据库作为缓存存热数据、磁盘数据库存全部数据的方案,在长期来看是会被“带缓存功能的磁盘数据库”取代的。因为只有两者合体才真正解决一致性的问题,同时业务上用着也更简单。当然另一个方向是“带冷数据持久化到磁盘功能的内存数据库”,这个方向我不看好,因为目前的内存数据库各方面还是和各种磁盘数据库差太多,而磁盘数据库其实已经普遍带各种程度的缓存功能,只不过单机的性能(缓存命中的话)和用C写的内存数据库有差距,并且redis的数据结构比较丰富。但是一旦性能差距逐渐缩小(比如很多新的数据库或者云服务厂商自己提供的数据库都是用C/C++写的),并且支持更多的数据结构,最终就可以彻底替换掉内存数据库了。