舍得网支撑1000万pv数据库缓存系统

说是支持1亿pv/天,也许有点夸张,也是为了吸引您能点进来,若是您能认真看完相信也不会让您失望,固然,确定有不少“高手”会对此会嗤之以鼻,不要紧,有不少眼高手低的人总喜欢评论别人却从不会看清本身。 

若是你们真想支持我、支持中国人开源项目,请把该文贴到本身的博客中或者收藏本文,记得包含文档的下载地址!!!!!!!谢谢。 

我说的系统主要是构建在hibernate之上的高效数据库缓存系统,其中包含了分布式解决方案,该系统已经应用在舍得网上了,没有发现大问题,本人也相信该系统已经足够强大,应付数百万IP/天的应用都不是问题,我这么说确定有人会对此表示怀疑,其实系统到底能撑多少IP/天不在于系统自己而是在于使用该系统的人。 
代码看上去很简单,其实倒是两年经验的总结,整过过程也遇到了不少难点,最后一一解决了,因此也请各位珍惜他人的劳动成果。本系统很是简洁易用,主程序 BaseManager.java不到1000行代码,用“精悍”来形容绝对不为过,1000行代码却包含了数据库对象的缓存、列表和长度的缓存、按字段散列缓存、update延时更新、自动清除列表缓存等功能,用它来实现像论坛、博客、校友录、交友社区等绝大部分应用网站都足够了。 

我在理想状态下作了压力测试,在没有数据库操做的jsp页面(舍得网新首页)里能够完成2000多requests每秒(正常状况可能有1/1000的 request有数据库查询,其他999/1000都是直接从缓存里读取),物品详情页每秒可完成3000多requests,纯静态html页面也只能完成7000多requests/秒,我对首页进行了三个小时的压力测试,完成了24850800个requests,java一点事都没有,内存没有上涨。按照2000个requests/秒算,一天按15小时计算,那么天天能完成3600*15*2000=1亿零8百万requests,固然这是理想状态,实际状态就算打一折,还能完成1000万pv/天,要知道,这只是一个普通1万3千块钱买的服务器,内存4G,CPU2个,LinuxAS4系统,apache2.0.63/resin2.1.17/jdk6.0的环境。 

如今进入正题。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 

为何要用缓存?若是问这个问题说明你仍是新手,数据库吞吐量毕竟有限,每秒读写5000次了不得了,若是不用缓存,假设一个页面有100个数据库操做,50个用户并发数据库就歇菜,这样最多能支撑的pv也就50*3600*15=270万,并且数据库服务器累得半死,搞很差何时就累死了。个人这套缓存系统比单独用memcached作缓存还要强大,至关于在memcached上再作了两级缓存,你们都知道memcached很强了,可是吞吐量仍是有限,每秒20000次get和put当遇到超大规模的应用时仍是会歇菜,本地HashMap每秒可执行上百万次put和get,在这上面损耗的性能几乎能够忽略不记了。舒适提示:能不用分布式的时候就不要用分布式,非用分布式的时候再考虑用memcached,个人缓存系统在这方面都已经实现了,改个配置就能够了,有兴趣的能够仔细测试测试! 

通常数据库缓存在我看来包含四种。第一种:单个对象的缓存(一个对象就是数据库一行记录),对于单个对象的缓存,用HashMap就能够了,稍微复杂一点用LRU算法包装一个HashMap,再复杂一点的分布式用memcached便可,没什么太难的;第二种:列表缓存,就像论坛里帖子的列表;第三种:长度的缓存,好比一个论坛板块里有多少个帖子,这样才方便实现分页。第四种:复杂一点的group,sum,count查询,好比一个论坛里按点击数排名的最HOT的帖子列表。第一种比较好实现,后面三种比较困难,彷佛没有通用的解决办法,我暂时以列表缓存(第二种)为例分析。 

mysql和hibernate的底层在作通用的列表缓存时都是根据查询条件把列表结果缓存起来,可是只要该表的记录有任何变化(增长/删除/修改),列表缓存要所有清除,这样只要一个表的记录常常变化(一般状况都会这样),列表缓存几乎失效,命中率过低了。 

本人想了一个办法改善了列表缓存,当表的记录有改变时,遍历全部列表缓存,只有那些被影响到的列表缓存才会被删除,而不是直接清除全部列表缓存,好比在一个论坛版(id=1)里增长了一个帖子,那么只要清除id=1这个版对应的列表缓存就能够了,版id=2就不用清除了。这样处理有个好处,能够缓存各类查询条件(如等于、大于、不等于、小于)的列表缓存,但也有个潜在的性能问题,因为须要遍历,CPU符合比较大,若是列表缓存最大长度设置成10000,两个4核的CPU每秒也只能遍历完300屡次,这样若是每秒有超过300个insert/update/delete,系统就吃不消了。 

在前面两种解决办法都不完美的状况下,本人和同事通过几个星期的思索,总算得出了根据表的某几个字段作散列的缓存办法,这种办法无需大规模遍历,因此 CPU符合很是小,因为这种列表缓存按照字段作了散列,因此命中率极高。思路以下:每一个表有3个缓存Map(key=value键值对),第一个Map是对象缓存A,在A中,key是数据库的id,Value是数据库对象(也就是一行数据);第二个Map是通用列表缓存B,B的最大长度通常1000左右,在B中,key是查询条件拼出来的String(如start=0,length=15#active=0#state=0),Value是该条件查询下的全部id组成的List;第三个Map是散列缓存C,在C中,key是散列的字段(如根据userId散列的话,其中某个key就是 userId=109这样的String)组成的String,value是一个和B相似的HashMap。其中只有B这个Map是须要遍历的,不知道说明白了没有,看完小面这个例子应该就明白了,就用论坛的回复表做说明,假设回复表T中假设有字段id,topicId,postUserId等字段(topicId就是帖子的id,postUserId是发布者id)。 

第一种状况,也是最经常使用的状况,就是获取一个帖子对应的回复,sql语句应该是象 
select id from T where topicId=2008 order by createTime desc limit 0,5 
select id from T where topicId=2008 order by createTime desc limit 5,5 
select id from T where topicId=2008 order by createTime desc limit 10,5 
的样子,那么这种列表很显然用topicId作散列是最好的,把上面三个列表缓存(能够是N个)都散列到key是topicId=2008这一个Map 中,当id是2008的帖子有新的回复时,系统自动把key是topicId=2008的散列Map清除便可。因为这种散列不须要遍历,所以能够设置成很大,例如100000,这样10万个帖子对应的全部回复列表均可以缓存起来,当有一个帖子有新的回复时,其他99999个帖子对应的回复列表都不会动,缓存的命中率极高。 

第二种状况,就是后台须要显示最新的回复,sql语句应该是象 
select id from T order by createTime desc limit 0,50 
的样子,这种状况不须要散列,由于后台不可能有太多人访问,经常使用列表也不会太多,因此直接放到通用列表缓存B中便可。 

第三种状况,获取一个用户的回复,sql语句象 
select id from T where userId=2046 order by createTime desc limit 0,15 
select id from T where userId=2046 order by createTime desc limit 15,15 
select id from T where userId=2046 order by createTime desc limit 30,15 
的样子,那么这种列表和第一种状况相似,用userId作散列便可。 

第四种状况,获取一个用户对某个帖子的回复,sql语句象 
select id from T where topicId=2008 and userId=2046 order by createTime desc limit 0,15 
select id from T where topicId=2008 and userId=2046 order by createTime desc limit 15,15 
的样子,这种状况比较少见,通常以topicId=2008为准,也放到key是topicId=2008这个散列Map里便可。 

总结:这种缓存思路能够存储大规模的列表,缓存命中率极高,所以能够承受超大规模的应用,可是须要技术人员根据自身业务逻辑来配置须要作散列的字段,通常用一个表的索引键作散列(注意顺序,最散的字段放前面),假设以userId为例,能够存储N个用户的M种列表,若是某个用户的相关数据发生变化,其他 N-1个用户的列表缓存纹丝不动。以上说明的都是如何缓存列表,缓存长度和缓存列表思路彻底同样,如缓存象select count(*) from T where topicId=2008这样的长度,也是放到topicId=2008这个散列Map中。若是再配合好使用mysql的内存表和memcached,加上F5设备作分布式负载均衡,该系统对付像1000万IP/天这种规模级的应用都足够了,除搜索引擎外通常的应用网站到不了这种规模。 

再次申明:系统究竟是不是强大不在系统自己而在于使用该系统的人!!! 

这个缓存系统是我和同事几年经验的总结,看似简单,其实也没那么简单,把它做为开源有下面几个目的:第一,真的但愿有不少人能用它;第二:但愿更多的人可以完善和改进它;第三:但愿你们能聚到一块儿为通用高效数据库缓存构架做出贡献,毕竟,数据库操做是各类应用最经常使用的操做,也是最容易产生性能瓶颈的地方。 

Zip包中包含了配置方法和测试用的jsp,只要把它配置成一个web应用就能够快速调试并看到缓存的力量了,文档和下载地址是http://shedewang.com/akaladocs/api/com/akala/dbcache/core/BaseManager.html。群组的地址是http://groups.csdn.net/shedewang_db_cache  配置说明文件在docs/开始配置.txt里有说明。  最后啰嗦一句,若是你们真想支持我、支持中国人开源项目,请把该文贴到本身的博客中或者收藏本文,记得包含文档的下载地址!!!!!!!谢谢。thank you and Good luck。
相关文章
相关标签/搜索