Discuz!下Memcache缓存实现方法


Posted in PHP onMay 28, 2010

前言
在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中增加

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

2.在include/common.inc.php中

$mem = new Memcache;
$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.完成了,测试吧!~

PHP 相关文章推荐
PHP中判断变量为空的几种方法分享
Aug 26 PHP
php获取域名的google收录示例
Mar 24 PHP
PHP使用GIFEncoder类生成的GIF动态图片验证码
Jul 01 PHP
PHP curl 抓取AJAX异步内容示例
Sep 09 PHP
PHP实现Soap通讯的方法
Nov 03 PHP
php的sso单点登录实现方法
Jan 08 PHP
php正则匹配html中带class的div并选取其中内容的方法
Jan 13 PHP
php使用标签替换的方式生成静态页面
May 21 PHP
浅谈使用PHP开发微信支付的流程
Oct 04 PHP
PHP表单验证内容是否为空的实现代码
Nov 14 PHP
PHP消息队列实现及应用详解【队列处理订单系统和配送系统】
May 20 PHP
PHP如何解决微信文章图片防盗链
Dec 09 PHP
备份mysql数据库的php代码(一个表一个文件)
May 28 #PHP
php下将图片以二进制存入mysql数据库中并显示的实现代码
May 27 #PHP
php set_time_limit(0) 设置程序执行时间的函数
May 26 #PHP
PHP 可阅读随机字符串代码
May 26 #PHP
PHP 一个随机字符串生成代码
May 26 #PHP
PHP 循环列出目录内容的函数代码
May 26 #PHP
PHP 删除一个目录及目录下的所有文件的函数代码
May 26 #PHP
You might like
暴雪前总裁遗憾:没尽早追赶Dota 取消星际争霸幽灵
2020/03/08 星际争霸
Extended CHM PHP 语法手册之 DIY
2006/10/09 PHP
PHP跨时区(UTC时间)应用解决方案
2013/01/11 PHP
PHP定时任务延缓执行的实现
2014/10/08 PHP
php实现QQ空间获取当前用户的用户名并生成图片
2015/07/25 PHP
PHP使用CURL实现下载文件功能示例
2019/06/03 PHP
用js实现判断当前网址的来路如果不是指定的来路就跳转到指定页面
2011/05/02 Javascript
jquery图片不完全按比例自动缩小的简单代码
2013/07/29 Javascript
javascript间隔定时器(延时定时器)学习 间隔调用和延时调用
2014/01/13 Javascript
jQuery插件echarts实现的单折线图效果示例【附demo源码下载】
2017/03/04 Javascript
Node.js 异步异常的处理与domain模块解析
2017/05/10 Javascript
Vue2.0用户权限控制解决方案
2017/11/29 Javascript
Vue 与 Vuex 的第一次接触遇到的坑
2018/08/16 Javascript
CKEditor4配置与开发详细中文说明文档
2018/10/08 Javascript
让 babel webpack vue 配置文件支持智能提示的方法
2019/06/22 Javascript
多个vue子路由文件自动化合并的方法
2019/09/03 Javascript
JS函数基本定义与用法示例
2020/01/15 Javascript
vue中的.$mount('#app')手动挂载操作
2020/09/02 Javascript
mustache.js实现首页元件动态渲染的示例代码
2020/12/28 Javascript
Python 由字符串函数名得到对应的函数(实例讲解)
2017/08/10 Python
python3 pandas 读取MySQL数据和插入的实例
2018/04/20 Python
PyQt5下拉式复选框QComboCheckBox的实例
2019/06/25 Python
Django RBAC权限管理设计过程详解
2019/08/06 Python
vue常用指令代码实例总结
2020/03/16 Python
Django REST Framework 分页(Pagination)详解
2020/11/30 Python
使用numpngw和matplotlib生成png动画的示例代码
2021/01/24 Python
华美博弈C/VC工程师笔试试题
2012/07/16 面试题
大学生职业生涯设计书
2014/01/02 职场文书
2014政务公开实施方案
2014/02/19 职场文书
认购协议书范本
2014/04/22 职场文书
关于十八大的演讲稿
2014/09/15 职场文书
赵乐秦在党的群众路线教育实践活动总结大会上的讲话稿
2014/10/25 职场文书
暑期家教宣传单
2015/07/14 职场文书
理解深度学习之深度学习简介
2021/04/14 Python
MySQL之高可用集群部署及故障切换实现
2021/04/22 MySQL
SQL语句中JOIN的用法场景分析
2021/07/25 SQL Server