在公司内部项目实现过程中团队对SubSonic增加了分布式存储,透明对象缓存,透明查询缓存的支持;内部使用了两三年,并且在持续改进中。

MoSonic支持海量数据存储,在web 2.0常见场景中其透明缓存层亦可带来10倍以上的读取性能提高。

这里写blog记述一下。

改进参考/使用了:

 

 

其中Cache Money影响较大,故内部命名为MoSonic,Money-SubSonic。

=============

SubSonic是.net中一个相当流行ORM库,号称零代码。与其它ORM相比,SubSonic在易用性方面相当突出。

但这也不意味着SubSonic,或者说ActiveRecord风格的ORM仅能局限于中小网站的开发使用。

ActiveRecord实际上仅是Martin Fowler在《企业应用架构模式》一书中的一个小章节提出的,两三页纸而已。

但在Web 2.0时代,随着被Ruby On RailsDjango等流行框架采用,而大放异彩。

Twitter建站之初也是采用Ruby On Rails,使用的也是RoR内置的ActiveRecord风格ORM。

随着Twitter的发展,数据库性能这块也必然成为瓶颈。而Twitter对此的回答是Cache-Money,一个透明的ActiveRecord缓存层。

Cache-Money能够透明的支持两类缓存,Object Cache 跟 Vector Cache。

(当然Cache-Money的作用不止这两类缓存。)

=============

都是ActiveRecord,上层API风格一致,Twitter可以在RoR的ORM中做的实现,也一定可以在SubSonic上用c#做实现。

Object Cache比较好理解,它仅仅是基于对象主键缓存对象内容。Object.FetchById(XXX) 这类函数中做下手脚,先查询缓存,缓存不存在时,再去查询数据库。这也是所谓的Read-Through直读缓存。

而Object.Save()在数据库保存成功之后,也随即更新Cache。这也是所谓的Write-Through直写缓存。

Object的直读/写缓存实现非常简单,对SubSonic的FetchById添加Object Cache的话,仅需要十来行代码就可以使用单机内存做缓存。

单机内存非常有限,Web 2.0网站一般使用memecached做分布式缓存,解决单机缓存内存有限的问题;这基本是标配了。

就.Net而言,找个靠谱的memcached client的要比实现SubSonic的Object Cache更加重要。

我使用的是BeIt出品的memcached客户端;据说性能更好的有EnyimMemcached,但我没有详细测试过,不好评论。

使用了Memcached做缓存的话,对象的序列化问题就变得非常突出了。

.Net内置的BinaryFormatter性能非常差,必须另寻序列化方案。

考察过诸多序列化方案后,我最终选择的是Facebook开源出来的Thrift;它的设计完备,跨语言/平台支持能力非常好,性能不比Google开源出来的ProtoBuf差,也可以扩展为进程间RPC通讯方案。

为SubSonic添加对象Thrift序列化相对来说就比较麻烦些,但也不难,在SubSonic的代码生成模板中修改即可。

Thrift本身实际上也有提供c#的对象序列化的代码生成工具;但直接使用的话,意味着需要在Thrift对象/SubSonic对象间多增加一次转换;修改代码量虽然会少些,但不如直接修改SubSonic的代码生成模板,直接将Thrift序列化的方法跟SubSonic对象做彻底的整合效率来得高。

使用Thrift做序列化跟使用.net默认的BinaryFormater性能差别是巨大的。公司两台Web服务器,在使用BinaryFormater时CPU近乎100%,但改用Thrift做序列化后立刻下降至20%。

(团队倒不是在出现服务器性能问题后才做了Thrift序列化;提前一年就做了;只是因故做了次实际的性能测试。)

==========

既然修改了SubSonic的代码生成模板,我也顺便将其Object new()的API干掉了;强迫开发者必须使用FetchById风格API获得对象。

同时也增加了FetchByIds的新函数,支持同时获得多个对象,对应select * from tables where id in (..., ...)的查询,以及memcached multi_get的命令。

通过添加透明的Object cache在不增加额外的业务逻辑代码情况下,已经可以获得显著的性能改善,但它还不是本质性的改进。

Cache Money真正的神器是其Vector Cache,实际上,Twitter团队在开发Cache Money时,是优先考虑了Vector Cache的实现,然后再考虑Object Cache;因为他们认为Vector Cache会性能影响更大,事实也证明他们判断正确。

下篇会继续讲Vector Cache的实现。