当一个网站达到一定级别后很容易出现效率问题,各个环节都有可能:比如数据库,前台服务器负载过 高,IO吃紧;数据增大缓存不够用或者用来同步数据的网络开销过大等。下面就自己项目遇到的问题和可采取的优化办法进行讨论,欢迎指教。(我们的项目目前 日pv在2000w左右)
最开始的时候,我们的项目拥有两台前台服务器,采用dns分流的方法做负载均衡。随着流量增加,前台两台服务器变得异常得卡,访问页面延迟增加,服 务器上明显表现就是cpu负载很高,idle几乎保持接近0.登录到服务器操作一些东西反应很慢。这个时候明显服务器不够用了,同时因为前台服务器响应缓 慢,造成很多请求拥堵,因为前台和数据库之间是使用长连接,所以间接也影响到了数据库,导致数据库经常超出连接数(我们设置的连接数是800)。当然要增 加机器,我们增加了2台服务器,同样做dns分流,这样就有2台电信,2台网通来服务。所以第一条就是增加硬件资源,简单高效。
接下来,随着流量继续攀升,发现数据库连接数一直维持在比较高的水平,很容易就爆了。而且数据库渐渐表现出来IO繁忙,读大概是6-10M/s,因 为代码上暂时没有做分库考虑,所以我们准备将用来备份的从数据库用来服务前台读请求。好在代码读写是分离的(也就是每个cgi连到数据库都是2个连接,一 个写,一个读),所以很容易就可以把前台的读请求切换到从数据库,这样主数据库一下就清闲了下来,主数据库主要负责写操作,解放了主数据库,主数据库从此 悠闲,而从数据库从此肩负了前台大量的读请求。
后来,我们发现从数据库因为读的负担比较重,导致从数据同步主数据库数据出现延迟,这个时候我其实考虑过有没办法修改mysql源码,提高同步线程 的优先级来解决。最终没有这么做,因为我们很快加入了新的一台从数据库,现在是两台从数据库来分担前台的大部分读请求,因此情况得到缓解。
再后来,流量继续爬高,导致两台从数据都出现同步延迟了。当然第一反映就是:再加从数据库?再加数据库也许是个解决办法,但是同时也要考虑其他问题,比如:如果都从主数据库同步,会不会增加主数据压力?我们尝试了以下几种办法:
1.静态化页面
我们的项目类似于新闻系统,但是又有区分。所以我们考虑静态化列表页面,每10分钟重新生成一次。结构大致是这样的:每隔10分钟,A机器就刷一次 数据库,重新生成列表页面,然后把这些页面同步到B,C等前台机器上,然后前台机器同时设置http expire头来减少浏览器的请求,设置last modify头减少网络传输。这样做是有效果的,一方面减轻了前台压力,同时也减少了数据库请求。但是发现了另一个问题:A机器定期会对数据库造成一个洪 峰式的冲击,而且这种主动生成方式非常盲目,另一方面要生成所有的列表页面,在不压缩页面的情况下大概好几G,要在几分钟内生成好几G的页面同时同步到前 台各个机器,这对A机器和前台都是很大考验。实际当中我们也发现,只是生成了部分列表页面,A机器已经比较繁忙了。
2.mod_cache
和静态化类似,使用mod_cache可以改主动为被动,使得生成页面不再盲目。结构大致是这样:前台四台服务器都配置mod_cache,缓存5 分钟,每当本地没有该页面或者已经过期的时候就去请求A机器拿新的页面并缓存到本地。这种做法代码改动很小,而且作为apache模块,不需要考虑 fastcgi或者wsgi建立新进程来服务的开销(指前台机器上)。考虑到有4台服务器都从A机器上拿,实际上A机器上面还可以做一层 mod_cache,即前台请求A,A请求自己生成静态页面,然后把静态页面返回给前台,剩下的前台请求A的时候就直接拿已经生成的静态页面就可以了。这 种方法很遗憾没有很好地实际测试过,但我觉得是可行的,特别是如果能将生成的静态页面再做gzip压缩,减少A机器和前台的网络传输消耗效率会更高。
3.memcache
memcache和mod_cache其实很像,一个是使用内存,一个是使用磁盘。出乎我们意料的是,相比静态文件好几G的磁盘消耗,使用memcache缓存到内存只占了几百M,这都归功于memcache的压缩。
使用memcache,项目的结构和mod_cache也很像,前台收到请求后去内存拿数据,拿不到则从数据库拿然后再设置到内存。这台 memcache机器就像第二点当中那台A机器,但是使用memcache本身有以下几个优势:第一,数据传输已经被压缩,占用内存小,网络传输消耗小; 第二,从实际观察发现,memcache这台服务器非常高效,占用cpu极低,和使用mod_cache相比,相当于把A机器的压力分散到了前台机器上 (使用memcache仍然都是前台在请求数据库,使用mod_cache则只有A服务器在请求数据库)。这样的话分散压力,提高效率。
实际测试发现,最终上面三种都没用在我们项目中,memcache也没有。原因是我们的项目有点特别,类似于新闻但又不完全相同,类似于游戏分区, 每个分区里面有自己的列表,memcache命中率稳定在65%左右,因为尽管总访问量比较大,但是分散到每个区,再到每个区里面每个列表,缓存的命中率 并不是很高。
最终我们又回归到了最开始的做法,我们增加了一台比较强悍一点的机器来做从数据库,事实也证明了这样可行。原因是我们发现从数据肩负了主要的读请 求,但是之前的从数据库机器都比较烂,4G的机器,2核至强cpu,都是游戏淘汰下来的机器,平时负载在3-4,而且使用了比较多swap,这说明机器内 存可能不足了。新机器换用8G内存,4核至强cpu,现在一台机器担当了4台前台服务器的读请求,负载稳定在0.3。
将来,我觉得我们的方向仍然是增加缓存,改变缓存的策略,考虑到memcache这么节约内存,可以把常用的请求对象全部丢到内存里面,相当于内存 里保存了数据库部分数据的完整拷贝,这样列表的请求和详细页面的请求都只靠请求memcache就可以完成,不像之前提到的使用memcache,仍存在 大量对数据库的请求,同时缓存的内容过期率比较高。这样做相当于mysql的query cache的一个延伸了。
总结下,我觉得一些可行的网站效率优化办法:
1.硬件是硬道理
2.使用从数据库分离读写请求
3.静态化页面
4.使用memcache或者mod_cache(即使用内存或者磁盘来做缓存)
转自:http://blog.moonforest.org/tag/memcache/