Discuz!的Memcache缓存实现

在PHP+MySQL架构的站点中,本文重点从MySQL的角度去分析如何使Discuz!论坛(或者相似的PHP+MySQL架构的程序)应对大访问量。同时给出一些使用Memcache去减轻MySQL压力的建议。其中不少数据是我的测试的结果,若有不一样意见,敬请留言告之。另外因为我的思惟的问题,行文比较跳跃,特此声明!php

系统分析
单纯的从MySQL的角度出发,单台MySQL的数据库负载到天天上亿次的操做(每秒大概1100次MySQL操做,而后乘以86400)应该不是很是困难的事情。按照这个数据也就是说一个单MySQL服务器的论坛来讲能够跑到2千万PV是不成问题的,我相信国内绝大部分的论坛都不可能作到天天2千万的PV,但实际状况并非如此。当论坛PV超过百万的时候一台WEB早已经不堪重负了。前端

就我手头的一些数据显示,目前的Discuz!论坛的基本服务器架构是前面Squid顶着,后面才是一台DB在撑着。这种架构中,web服务器压力增大能够经过并行增长服务器解决,而MySQL压力却无处释放,在不考虑MySQL官方服务的状况下,咱们经过合理的利用Memcache是能够达到减轻MySQL服务器负载的。mysql

可能会有朋友说咱们能够对数据表进行分表(注:此处分表是指经过PHP程序去分表,好比pw,dv的分表)处理,可是当前的状况是一台DB服务器已经不能支撑当前的数据处理了,经过PHP对MySQL进行的分表依然不能减轻MySQL的负载。(注:本段文字针对已经成型的系统,若是是独立开发的系统在架构前期就进行数据的同步分区仍是不错的。web

还可能有朋友会说利用MySQL的主从构架,若是你提出这个问题,我就很明确的告诉你,回去看看手册吧。在Mysql Master/Slave 模式中,Slave主要是来备份数据的,只有当Master出现故障时,Slave才会接过Master的服务,对外部请求进行处理,直到Master恢复正常。就是说:在Master/Slave中,要么是Master在服务,要么是Slave在服务,不会Master/Slave同时提供服务。使用MySQL主从依然不能有效的下降MySQL的负载。sql

或许你又会问我为何不使用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中增长
ide

  
  
           
  
  
$memcachehost ='127.0.0.1';
$memcacheport =11211;
$memcachelife =60;

2.在include/common.inc.php中
  
  
           
  
  
$mem =newMemcache;
$mem->connect($memcachehost, $memcacheport);

3.修改include/db_mysql.class.php中的fetch_array、query这两个方法,并添加query_mysql方法,代码以下:
  
  
           
  
  
function fetch_array($query, $result_type = MYSQL_ASSOC){
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查询的代码由
  
  
           
  
  
$db->query(

修改成
  
  
           
  
  
$db->query_memcache(

注意并将
  
  
           
  
  
while($post = $db->fetch_array($query)){

修改成
  
  
           
  
  
foreach($query as $post){

没有while的$db->fetch_array能够不用修改。

 

下面代码有用得着的就拿去:

  
  
           
  
  
preg_replace("/while\([$](\w+)\s*\=\s*[$]db->fetch_array\([$]query\)\)/is","foreach(\$query as \$\\1)", $file);

回头放出个小工具批量替换下就能够了。在EditPlus中能够这样替换:while\([$](.*) = [$]db->fetch_array\([$]query\)\)替换为foreach($query as $\1)5.完成了,测试吧!~