Discuz!的Memcache缓存实现
前言:
在PHP+MySQL架构的站点中,本文重点从MySQL的角度去分析如何使Discuz!论坛(或者类似的PHP+MySQL架构的程序)应对大访问量。同时给出一些使用Memcache去减轻MySQL压力的建议。其中很多数据是个人测试的结果,如有不同意见,敬请留言告之。另外由于个人思维的问题,行文比较跳跃,特此声明!
系统分析:
单纯的从MySQL的角度出发,单台MySQL的数据库负载到每天上亿次的操作(每秒大概1100次MySQL操作,然后乘以86400)应该不是非常困难的事情。按照这个数据也就是说一个单MySQL服务器的论坛来说可以跑到2千万PV是不成问题的,我相信国内绝大部分的论坛都不可能做到每天2千万的PV,但实际情况并不是如此。当论坛PV超过百万的时候一台WEB早已经不堪重负了。
就我手头的一些数据显示,目前的Discuz!论坛的基本服务器架构是前面Squid顶着,后面才是一台DB在撑着。这种架构中,web服务器压力增大可以通过并行增加服务器解决,而MySQL压力却无处释放,在不考虑MySQL官方服务的情况下,我们通过合理的利用Memcache是可以达到减轻MySQL服务器负载的。
可能会有朋友说我们可以对数据表进行分表(注:此处分表是指通过PHP程序去分表,比如pw,dv的分表)处理,但是当前的情况是一台DB服务器已经不能支撑当前的数据处理了,通过PHP对MySQL进行的分表依然不能减轻MySQL的负载。(注:本段文字针对已经成型的系统,如果是独立开发的系统在架构前期就进行数据的同步分区还是不错的。)
还可能有朋友会说利用MySQL的主从构架,如果你提出这个问题,我就很明确的告诉你,回去看看手册吧。在Mysql Master/Slave 模式中,Slave主要是来备份数据的,只有当Master出现故障时,Slave才会接过Master的服务,对外部请求进行处理,直到Master恢复正常。就是说:在Master/Slave中,要么是Master在服务,要么是Slave在服务,不会Master/Slave同时提供服务。使用MySQL主从依然不能有效的降低MySQL的负载。
或许你又会问我为什么不使用MySQL集群(MySQL Cluster),那可是白花花的银子啊,同等金钱的付出下,获得最大的收益才是王道。PS:说句题外话,MySQL手册中将MySQL集群解释为MySQL簇,不习惯。
其实在MySQL5.1中的MySQL分区(MySQL Partition)是个很好的东西,它允许根据可以设置为任意大小的规则,跨文件系统分配单个表的多个部分。实际上,表的不同部分在不同的位置被存储为单独的表。我认为这个才是当前情况下,最积极有效的降低MySQL负载的解决方法之一。但是遗憾的是,这种MySQL分区的方式我个人没有使用过的经历,也不见有相当充分的案例表明它是稳定的或者不稳定的。所以我还在徘徊中。如果你知道,请麻烦告之!有朋友说腾讯是在用MySQL分区,但是遗憾的是我没有得到确切的数据。
好了分析总结了这么多种降低MySQL负载的方式之后,在用户环境需求等特定条件下,我得出结论在当前情况下,缓解Discuz!论坛的MySQL负载比较有效的方法就是使用Memcache!
使用Memcache的理由:
1.Web Server(Lighttpd、Nginx据说都比Apache效率高好多,大家可以试用下)对CPU要求高,对内存要求低;而Memcached Server是对CPU要求低,对内存要求高,所以可以搭配使用。在对前端的Web Server上安装Memcached Server是可行的。
2.金钱金钱金钱,最少的付出,获得最大的收益。
3.简单简单简单,对于一个架构合理的系统来说,添加Memcache的支持可能只是一个批量处理文件的过程
Discuz!使用Memcache
1.在config.inc.php中增加
$memcacheport = 11211;
$memcachelife = 60;
2.在include/common.inc.php中
$mem->connect($memcachehost, $memcacheport);
3.修改include/db_mysql.class.php中的fetch_array、query这两个方法,并添加query_mysql方法,代码如下:
return is_resource($query) ? mysql_fetch_array($query, $result_type) : $query[0];
}
function query_memcache($sql, $type = '') {
global $mem,$memcachelife;
$key = md5($sql);
if(!($query = $mem->get($key))) {
$query = $this->query($sql, $type);
while($item = $this->fetch_array($query)) {
$res[] = $item;
}
$query = $res;
$mem->set($key, $query , 0, $memcachelife);
}
return $query;
}
function query($sql, $type = '') {
global $debug, $discuz_starttime, $sqldebug, $sqlspenttimes;
$func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ?
'mysql_unbuffered_query' : 'mysql_query';
if(!($query = $func($sql, $this->link)) && $type != 'SILENT') {
$this->halt('MySQL Query Error', $sql);
}
if(substr($sql, 0, 6) == 'SELECT') {
echo '<font color="red">Cache SQL</font>:<font color="green">'.$sql.'</font><br /><br />';
} else {
echo '<font color="red">Flash SQL</font>:<font color="green">'.$sql.'</font><br /><br />';
}
$this->querynum++;
return $query;
}
4.将需要使用Memcache缓存的SQL查询的代码由
修改为
注意并将
修改为
没有while的$db->fetch_array可以不用修改。
下面代码有用得着的就拿去:
回头放出个小工具批量替换下就可以了。
在EditPlus中可以这样替换:while\([$](.*) = [$]db->fetch_array\([$]query\)\)替换为foreach($query as $\1)
5.完成了,测试吧!~
参考资料:
对Memcached有疑问的朋友可以参考下列文章:
Linux下的Memcache安装:http://www.ccvita.com/257.html
Windows下的Memcache安装:http://www.ccvita.com/258.html
Memcache基础教程:http://www.ccvita.com/259.html
Discuz!的Memcache缓存实现:http://www.ccvita.com/261.html
Memcache协议中文版:http://www.ccvita.com/306.html
Memcache分布式部署方案:http://www.ccvita.com/395.html
后记
写完之后突然发现天已经要亮了,闷骚了一个晚上。个人的一些总结,欢迎留言探讨!
辛苦了!能坚持到天亮,不错
不错,sql(1-100)条数据,但如果其中有一个数据进行了更新,而其sql语句仍然一样,可能会读到脏数据。要一个update del,触发memcached的sql失效。
楼上正解,我那段代码只是最简单的利用Memcache减轻MySQL负载的一个小例子,对于一个论坛来说用1分钟的延迟时间减轻很多MySQL的压力,我认为是值得。当然程序可以做得更完美些(也就是更复杂些),去维护Memcache的key的时效性。
我正在进一步的整理中,可以过短时间再来看看
水水同志辛苦了
虽然偶不懂这么高深的理论
但是文章很不错啊
小青年很有想法嘛
靠
原来在闷骚这个啊
能不能再完善些
好象很多地方都涉及到用户权限的问题
能不能举个实例比如
index.php
和forumdisplay.php的哪些地方能使用memeched的代码
給您留的
被您删除了,不知道为什么
没有被删除,只是还没有审核通过而已
不设计权限判断的问题,我只是把从MySQL中取得的数据通过Memcached缓存了,该判断的权限还是PHP去做的,没有改变
谢谢
已经领悟到了
怎样判断key已经过期?
谢谢
key过期的时候,你是get不到的,比如$mem->get($key)返回FALSE,这时候就是key过期了
明白了
谢谢
现在想把我自己的DZ改造下
我去参考VBB弄
memcache的确能够有效的降低mysql的查询压力,但是优秀的缓存机制一定要考虑什么时候缓存,以及缓存过期的问题。要不然对用户来说就是非常不友好的。
一片很有趣的文章。
BTW: 1100次MySQL操作/S, 对于一台 DB 来说多少有些不现实.
在Master/Slave中,要么是Master在服务,要么是Slave在服务,不会Master/Slave同时提供服务
这种说法是有问题的。master处理写,slave处理读的读写分离方式是可行的,实际上有不少架构就是这么来处理多并发访问的。
TO Fenng : 每秒1100次MySQL操作还是可以实现的。这个数据是针对Discuz!论坛的数据表的,我有数据,只是跑起来有些吃力,嘎嘎!
TO NinGoo:其实我是想说对于写操作来说,主从不会同时起作用,这里写的是有问题。感谢提醒
呵呵,对于论坛应用来说,读的压力应该要远远大于写的压力的吧
个人认为,主从库的有效分离是减轻mysql压力的较为有效的办法.
memcache有的时候我觉得有点鸡肋的感觉,当然,并不是说memcache不行,而是像上面有位朋友所说的,cache的最高境界不在于它本身能减少多少数据库查询,性能有多好,而是在于你如何根据自身情况去提高cache命中率以及控制缓存点(包括时间以及要缓存的部位)
多台读库和少量写库的(M/S)搭配是大论坛或大社区有意义的尝试.而且,mysql数据库”集群”的成本只在服务器的价格与维护上,并没有软件上的成本.并不会有更多的金钱开销,呵呵.而且,在程序中实现读库与写库的不冲突也简单易行:
对于数据同步的具体需求分为两种模式 —-
1.如果只是考虑你写入后,用户立即读取这种情况,应该是完全来的及的,因为这个过程数据复制完成不超过一个毫秒
2.如果你的同一个程序中 insert 或者 update 后,立即就有关联的其他查询,那么延迟会造成影响,所以需要在程序中加一个判断(我随手写的一个示意,不必深究代码,呵呵):
while (! mysql_num_rows($result))
{
if ($times++ >= $timeout) break;
usleep($read_delay_time);
$result = mysql_query($query, $link_r) or die(”Query execute error: ” . mysql_error($link_r));
}
这样确保了读,写,感觉上还是很ok.当然数据库前面在放上memcache,效果自然更ok!
胡乱写了点,大家凑合看…
以上.
好难理解啊。郁闷
好文,收藏至20ju.com
:)apc 这种缓存方式又怎么样呢?
你没有考虑如果memcache服务器挂了的情况
在memcache服务器挂了的情况下,按照我上面文章中写的东西
所有的内容都是直接从MySQL服务器直接查询了,因为链接Memcache的操作已经超时
但是这样处理显然没有解决问题,在不考虑网速的情况下,虽然可以设置超时的时间但是其所带来的用户体验问题还是值得关注
现在看来这篇文章写的很多粗陋的地方,所以决定过段时间重写下
期待KIMI完善这个文章。先收藏了
每秒大概1100次MySQL操作,然后乘以86400,跑一天??服务器恐怕早就挂掉了,不要误导我们新手好不好?
@www 恩 每秒1100的并发是有点高了,但一台DB确实可以撑住,请记住Discuz!的所有数据库操作中,大概有三分之一是对cdb_session表的操作,而且对于不同性质的论坛,数据库的访问压力侧重也是不同的。我觉着还是有一定参考价值的,不存在什么误导之类吧
请记住Discuz!的所有数据库操作中,大概有三分之一是对cdb_session表的操作,那你何必搞得这么复杂,就只对cdb_session的select做memcache,不就可以了么?所以呢。标题就不要起的这么大么。应该叫做:Discuz!的cdb_session表的Memcache缓存实现。所以呢。还是误导。鉴定完毕。
你也不要想什么对策了。本来就是啊。其他的表确实也没怎么操作数据库。都生成缓存文件了。我没说错吧。所以请修改下标题吧。否则呆滞康会笑话你的哦。。嘎嘎
哦。不好意思。我说错了。对cdb_session的select做memcache,那就失去意义了。所以。标题是在做无用功。故。没有参考价值。嘎嘎
@www 你已经无敌了,Discuz!论坛程序的MySQL的一般瓶颈都是在cdb_posts cdb_members等数据表等的联合查询上,还有threads表的操作。尤其是对threads表进行memcache还是很有必要的。据我所知,就有个很大的论坛是这样做的。
另外我举例cdb_session是想说说明对于Discuz!的数据库进行优化,还是需要有侧重点的。
本文写的也确实很稚嫩,去年12月我还没有发育的很好
顺便再说下,你无敌了。
去年12月我还没有发育的很好,不知道是我无敌,还是你发育太快,照这样发育,也快无敌了。HOHO“`
我请教个问题可以不,多少访问量,用memcache.如果Master/Slave这样的架构,还需要使用你这个咚咚么。这个问题请你发育好了,再回答我。嘎嘎
垃圾验证码,经常出’traffic’,你脑袋秀豆了么?
哈哈。快换个验证码吧。。还有尽快回答陛下的问题。。。嘎嘎
看来不是我一个人说验证码有问题……
就是你一个人啊,ou是神仙。。嘎嘎
@www 如果按照Master/Slave之为了分流MySQL的压力,Memcache也是为了分流MySQL的压力,方案不一样,具体问题具体对待,也不存在哪一个架构就一定能够解决所有问题的。
另外说,在我手头的一些文档和数据充分显示从百度,腾讯这些站点目前对于MySQL的负载的解决途径就是Master/Slave+Memcache一起用,另外硬件的支持也很重要,比如目前bbs.xunlei.com就是做了raid5,然后把MySQL丢到内存里去做的负载减压
@嘟嘟鱼 莫非验证码真的有问题么?可是我测试了几回都没问题啊~!
再PS下:迅雷官方论坛是也同样做了Master/Slave,没用Memcache
第一次接触大网站的具体优化,对新手确实很有参考价值,特别是后面的讨论!不知道你们是不是php程序员啊?
@phpgrid 基本上应该都是PHP,还有几个高手是DBA
发现水水blog的技术含量越来越高了,以后开来要经常关注了,吼吼。。。。
最近也是遇到百万级别以上的访问,曾经也是想要主从库来抵抗大并发(原谅我用抵抗这个词吧),但是发现效率也不是太高,如果写的多了,几乎都浪费在Master向Slave向分发数据,也可能是这个项目的实时统计数据过多吧!最近也接触了一段memcache,调查了一下,可能会在以后使用,希望多多发些这方面的资料!
ps:调查到一些大型网站用memcache的案例
Livejounal(memcached的作者)
12台普通server,28个 memcached实例 , 30 GB的缓存数据,90-93%缓存命中率
Youtube
据说大量使用,没有获取相关数据
Facebook
400 memcached server,> 5TB 的缓存数据,并且增加了udp版的memcached改进
Wikipedia
30 servers,60GB的缓存数据,
LZ有一点没说,memcache 的重要特性 分布式存储—-多个服务器共享一个hash表。
最早php 的 官方memcache 是没有实现这个功能的,最近也有了。
可能你写文章的时候还没有。
而memcached 服务器端,是原本就支持分布式存取的,这也是memcache可以缓解压力的重要利用点。
PS 上面某些人根本没看过memcache就胡乱发言,不懂得情况下,尽量少发言。
@ll, 貌似memcached服务器端没有实现所谓的分布式存取,而是由客户端去的代码库去实现的,你可以去看看文档。
我认为用memcache去缓解加速是一个很好解决方案.
所有对象从内存中拿出来这个速度是最快的.
没有什么对象从数据库中拿出来是有一种对于高访问中一个性能很差.
所以对于Master/Slave这个性能来说对于一个并发100000万以上网站还是有点性能上存在问题.
我可以向大家说明facebook网站是通APC->Memcache组合来分担服务器压力,当然他们数据库服务器也主/从服务器模式.所以这里Memcache还是一种非常好解决方案.
但是在代码上写的还是有点问题?呵呵
所以来说大型网站是没有不会使用Memcache的…
对于discuz来说,应该也有这样一个功能设计.
因为要是反这个discuz一改到一个大型社区中.海量数据分布缓存对于用户访问性能肯定有一个大幅度增加,呵呵
請問能否推出 6.1 memcached? 十分感激…
那个www是个很菜的鸟。可能还是个鸟蛋。什么都不懂。呵呵。
APC->Memcache
你说错了
apc和memcache是并行的
apc缓存php编译结果
memcache缓存数据
楼上有位同学说的很好
就是memcache可以分布式 我个人觉得 这个是最重要的
@刚看到 APC->Memcache总->的意思不是方向,是执行的过程.
最近看到的Memcachedb和Tokyo Tyrant来做memcache的分布式都是很不错的。
你好kimi,很想问下function fetch_array($query, $result_type = MYSQL_ASSOC) {
return is_resource($query) ? mysql_fetch_array($query, $result_type) : $query[0];
}
里is_resource($query)的意思是什么?
非常感谢!
当然我想澄清一下…
APC->memcache
实际我个人认为不单纯一个并行关系,如果服务器多了你说这个并行到底是一种什么关系?
最近也是一直在看一些文档,有关这方面更好设计方案….
哈哈…
有一点我认为facebook网上发布那个架构图好像有所保留…
当中有一些东西最重要东西没有写全…
这也就是老外一个狡猾之处了…
不过他向大家介绍APC一个运转过程和memcache还是值得我们要好好研究一下的.
@雪碧之爱 我最近有不少实际经验,找时间写出来~
query_memcache函数有个问题,如果query($sql, $type)为空,那么设置缓存的值也是空,下次还是会执行SQL的
第三步3.修改include/db_mysql.class.php中的fetch_array、query这两个方法,并添加query_mysql方法,代码如下
请问db_mysql.class.php中的fetch_array、query这两个方法如何修改?是把你下面的代码拷贝进去覆盖吗?还是怎么样?
我想问一个问题,像discuz这样的代码,最开始根本没有考虑到用cache,如果要解决缓存一致性问题怎么搞?
比如刚执行了
select * from table
缓存了这个结果,
然后执行了
delete from table where id=1
然后再执行
select * from table
岂不是会出现不一致?
在这个时候用memcached是不是成本太大了一点?
我现在就有这样的需求,但是要解决一致性问题,基本上很多代码都要重新写了,是这样吗?
@QQ 是存在这个问题哦
@freebird 是的,按照文中我贴出来的代码进行修改!
@NB 这2个SQL在Memcache中的key是不一样的,不会重新执行!
不错的文章。特别是后面评论的筒子们,都比较有见解。
php+memcache+mysql,的确是可以解决很多网站大量的访问压力的。并且在用户的访问中,读远大于写的情况下,尤其明显。
当然,memcache的分布式存储也是优点多多。
mysql的M/S功能,对于保证网站的实时服务是有帮助的。服务器在运行,难免会碰到死机的情况,到时M/S功能就派上用场了。
我觉得,有空的时候,应该多研究一下如何优化memcache,最大化利用才是王道。
楼主厉害哦! 交个朋友吧,qq:509240
在下正在找 Lighttpd或Nginx + Memcache 构架的高手。
文章不错,后面的回复更棒!
3q楼主,这篇文章对我十分有用
数据如何才能实时
帖子发表好要等 缓存更新才会显示 这样不是很理想啊
@uuuuu, 对于比较大的论坛来说,帖子列表缓存五分钟确实是不可以接受的,另外可以新建新帖缓存,总之可以通过定制PHP来实现你想要的目的。
能简单说说 方法吗
能简单说说 方法吗 谢谢
@uuuuu, 说什么方法,我没明白。。。
好东西,学习了
Master/Slave 的架构是Master负责write操作 slave负责read操作,因为一般来说read操作是主要的,而slave是可以设立很多台的,当然是有效果的。
文章不错 thanks !
这个地方可以偷懒一下:让发布者可见(本地化实现),待判断完缓存重建完成然后再显示,或者以一个session存活期为限制,毕竟那种刚发帖子就有人嗷嗷回的情况太少,而且这个时间与其让等待不如让不知道!
@丹木秋风, 恩,让用户可见自己发帖,这项想法还是比较实际有用的。
回复留言
水水语录
你脑袋有坑,像月球表面一样
最新日志
分类列表
文章归档
关键词
Apache 媳妇 PHP技术 感慨 配置 PHP类 css Memcache MySQL 调优 优化 陈陈 迪拜 生活 Linux PHP MySQL优化 Discuz! MooPHP框架 缓存 Rewrite Nginx JavaScript FireFox blog程序 MySQL效率 WordPress插件 风骚 负载 视频 生日 WordPress ZIP yy 服务器 高负载 性能 IE6 正则表达式 常用 IE7 风格 Cookie 安全 防盗链
Copyright © 2009 平凡的世界 Themes Design by 平凡的世界. Base on justintadlock.com.
Powered by WordPress. 19 queries. 0.246 seconds.