DISCUZ论坛使用memcached优化解决方案

转载本文请保留作者信息,违者必究

 

作者:覃学涵(http://www.3sys.cn/)

Email:[email protected]

 

如何让discuz支持千万级别的数据和大用户量访问呢?答案是使用memcached优化。本文讨论DISCUZ论坛使用memcached优化的解决方案。作者使用的discuz版本是7.0,  7.0之后的优化方法本文没有做介绍,可以参考本文,举一反三。

 

思路:

 

1.无需修改全站的代码,只对DISCUZ的MYSQL的数据库操作类修改,支持MEMCACHED。

2.有MEMCACHED总开关,需要的时候打开MEMCACHED缓存,访问量不大时,关闭MEMCACHED缓存。

3.为不影响用户体验,用户登录的时候关闭MEMCACHED缓存,不登录的用户访问,开启memcached缓存。

4.memcached开启可以防洪,应付大规模的DDOS攻击。

 

步骤一、安装memcached.

 

安装方法 参考本文作者,我写的文章,网址:

 

http://blog.csdn.net/IeSneaker/archive/2010/07/21/5753264.aspx

 

 

步骤二、修改discuz的配置文件

 

修改根目录下的config.inc.php,增加如下代码

 

 

// ============================================================================   
// memcache 改造   
// ============================================================================   
define("IS_RUN_MEMCACHE",true);  //是否开启memcached   
define("MEMCACHE_HOST",'127.0.0.1');  //memcache 服务器   
define("MEMCACHE_PORT",11211);  //memcache 端口   
define("MEMCACHE_LIFETIME",5*60); //memcache 时间   
define("MEMCACHE_PREFIX","MYSITE"); //cacheid使用的前缀 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/IeSneaker/archive/2010/07/29/5774295.aspx

 

步骤三、修改discuz数据库操作类

修改include/db_mysql.class.php文件,作者这里贴出这个类修改后的完整代码。

 

<?php

/*
	[Discuz!] (C)2001-2009 Comsenz Inc.
	This is NOT a freeware, use is subject to license terms
	
	$Id: db_mysql.class.php 16688 2008-11-14 06:41:07Z cnteacher $
	$modify by 覃学涵  网站: http://www.3sys.cn 
*/

if(!defined('IN_DISCUZ')) {
	exit('Access Denied');
}

class dbstuff {

	var $version = '';
	var $querynum = 0;
	var $link = null;
	var $memcache = null;
	var $isCache = false; //是否开启memcached
	var $arrCursor = array(); //数据游标
	
	function & get_memcache()
	{
		if (is_object($this->memcache) == false) {			
			$this->memcache = new Memcache();
			$this->memcache->connect(MEMCACHE_HOST, MEMCACHE_PORT);
		}
		
		return $this->memcache;
	}

	function connect($dbhost, $dbuser, $dbpw, $dbname = '', $pconnect = 0, $halt = TRUE, $dbcharset2 = '') {

		$func = empty($pconnect) ? 'mysql_connect' : 'mysql_pconnect';
		if(!$this->link = @$func($dbhost, $dbuser, $dbpw, 1)) {
			$halt && $this->halt('Can not connect to MySQL server');
		} else {
			if($this->version() > '4.1') {
				global $charset, $dbcharset;
				$dbcharset = $dbcharset2 ? $dbcharset2 : $dbcharset;
				$dbcharset = !$dbcharset && in_array(strtolower($charset), array('gbk', 'big5', 'utf-8')) ? str_replace('-', '', $charset) : $dbcharset;
				$serverset = $dbcharset ? 'character_set_connection='.$dbcharset.', character_set_results='.$dbcharset.', character_set_client=binary' : '';
				$serverset .= $this->version() > '5.0.1' ? ((empty($serverset) ? '' : ',').'sql_mode=/'/'') : '';
				$serverset && mysql_query("SET $serverset", $this->link);
			}
			$dbname && @mysql_select_db($dbname, $this->link);
		}

	}

	function select_db($dbname) {
		return mysql_select_db($dbname, $this->link);
	}

	function fetch_array($query, $result_type = MYSQL_ASSOC) {
		if ($this->isCache ) { //缓存
			$row = false;
			if (is_array($query)) {
				$cacheId = $query["cacheId"];
				$arr = & $query["result"];				
				$nums = $query["nums"];
				$cursor = $this->arrCursor[$cacheId];
				if ($cursor <= $nums-1) {
					$row = $arr[$cursor];//返回一行
					$this->arrCursor[$cacheId] = $cursor+1;//游标加一
				}				
			}
			return $row;
		}else {
			return mysql_fetch_array($query, $result_type);
		}
	}

	function fetch_first($sql) {
		return $this->fetch_array($this->query($sql));
	}

	function result_first($sql) {
		return $this->result($this->query($sql), 0);
	}

	function query($sql, $type = '') {
		$query = false;
		
		if (IS_RUN_MEMCACHE == false) { //总开关关闭缓存
			$this->isCache = false;
		}
		//echo "ALL:".$sql."<br>/n";
		if ($this->isCache && $this->is_cache_sql($sql)) {//有缓存
			$cacheId = $this->get_cache_id($sql); //cacheId	
			$this->arrCursor[$cacheId] = 0;			
			$query = & $this->get_cache($sql)	;	//缓存中读取
			if (!is_array($query)) {
				//取数据库				
				$res = & $this->_query_from_db($sql, $type) ;
				if (!$res) {
					return false;
				}
				
				//取出数组缓存到memcached
				$i = 0;
				$arr = array();
				while($row = mysql_fetch_array($res)) {					
					$arr[$i] = $row ; 
					$i++;
				}
				
				$query["cacheId"] 	= $cacheId;
				$query["sql"] 		= $sql;//SQL语句
				$query["result"] 	= $arr; //数据结果列表
				$query["nums"] 		= $i; //数据结果数量
				$query["fields_nums"] = mysql_num_fields($res); //字段数
				$query["fields"] 	= mysql_fetch_field($res); //字段
				
				$this->set_cache($sql,$query,MEMCACHE_LIFETIME); //缓存起来
			}else{
				//echo "Cache:".$sql."<br>/n";
			}		
			
			return $query;
		}else{
			$query = & $this->_query_from_db($sql, $type) ; //无缓存
		}
		return $query;
	}
	
	//数据库访问
	function & _query_from_db($sql, $type = '')
	{
		global $debug, $discuz_starttime, $sqldebug, $sqlspenttimes;
		

		if(defined('SYS_DEBUG') && SYS_DEBUG) {
			@include_once DISCUZ_ROOT.'./include/debug.func.php';
			sqldebug($sql);
		}

		$func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ?
			'mysql_unbuffered_query' : 'mysql_query';
		if(!($query = $func($sql, $this->link))) {
			if(in_array($this->errno(), array(2006, 2013)) && substr($type, 0, 5) != 'RETRY') {
				$this->close();
				require DISCUZ_ROOT.'./config.inc.php';
				$this->connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect, true, $dbcharset);
				$this->query($sql, 'RETRY'.$type);
			} elseif($type != 'SILENT' && substr($type, 5) != 'SILENT') {
				$this->halt('MySQL Query Error', $sql);
			}
		}

		$this->querynum++;
		return $query;
	}
	
	//从memcahce中取数据
	function & get_cache($sql)
	{
		$cacheId = $this->get_cache_id($sql);
		$ret = $this->get_memcache()->get($cacheId);
		if (is_array($ret) || is_object($ret)) {
			return $ret;
		}
		return false;
	}
	
	//memcache缓存数据
	function set_cache($sql,$data,$cacheTime=0)
	{		
		$cacheId = $this->get_cache_id($sql);
		if (is_array($data) || is_object($data)) {					
			$this->get_memcache()->set($cacheId,$data,0,$cacheTime);
		}
	}
	
	//判断是否SQL需要缓存,部分SELECT语句需要缓存
	function is_cache_sql($sql) 
	{		
		$sql = strtolower(trim($sql));
		$pat = "/^(select){1}(/s)*(/w|/*|/,|/(|/)|/s|/.|/=|0-9|a-z|A-Z|/')*(from)*/i"; //判断是否select语句
		if (preg_match($pat,$sql)) {			
			return true;
		}else{
			return false;
		}
	}

	function affected_rows() {
		return mysql_affected_rows($this->link);
	}

	function error() {
		return (($this->link) ? mysql_error($this->link) : mysql_error());
	}

	function errno() {
		return intval(($this->link) ? mysql_errno($this->link) : mysql_errno());
	}

	function result($query, $row = 0) {
		if ($this->isCache ) { //缓存
			$ret = false;
			if (is_array($query)) {
				$arr = & $query["result"];				
				$nums = $query["nums"];
				if ($row < $nums-1) {
					$ret = $arr[$row];//返回一行		
				}				
			}
			return $ret;
		}else {
			$query = @mysql_result($query, $row);
			return $query;
		}
	}

	function num_rows($query) {
		if ($this->isCache ) { //缓存
			$ret = 0;
			if (is_array($query)) {
				$ret = $query["nums"];								
			}
			return $ret;
		}else {
			$query = mysql_num_rows($query);
			return $query;
		}
	}

	function num_fields($query) {
		if ($this->isCache ) { //缓存
			$ret = 0;
			if (is_array($query)) {
				$ret = $query["fields_nums"];								
			}
			return $ret;
		}
		return mysql_num_fields($query);
	}

	function free_result($query) {
		if ($this->isCache ) { //缓存
			unset($query);
			return true;
		}
		return mysql_free_result($query);
	}

	function insert_id() {
		return ($id = mysql_insert_id($this->link)) >= 0 ? $id : $this->result($this->query("SELECT last_insert_id()"), 0);
	}

	function fetch_row($query) {
		if ($this->isCache ) { //缓存
			$row = false;
			if (is_array($query)) {
				$cacheId = $query["cacheId"];
				$arr = & $query["result"];
				$nums = $query["nums"];
				$cursor = $this->arrCursor[$cacheId];
				if ($cursor <= $nums-1) {
					$row = $arr[$cursor];//返回一行
					$this->arrCursor[$cacheId] = $cursor+1;//游标加一
				}				
			}
			return $row;
		}else {
			$query = mysql_fetch_row($query);
			return $query;
		}
	}

	function fetch_fields($query) {
		if ($this->isCache ) { //缓存
			$ret = false;
			if (is_array($query)) {
				$ret = $query["fields"];								
			}
			return $ret;
		}
		return mysql_fetch_field($query);
	}

	function version() {
		if(empty($this->version)) {
			$this->version = mysql_get_server_info($this->link);
		}
		return $this->version;
	}

	function close() {
		return mysql_close($this->link);
	}

	function halt($message = '', $sql = '') {
		define('CACHE_FORBIDDEN', TRUE);
		require_once DISCUZ_ROOT.'./include/db_mysql_error.inc.php';
	}
	
	function get_cache_id($sql)
	{
		$cacheId = MEMCACHE_PREFIX . "_".md5($sql);
		return $cacheId;
	}
}

?>

步骤四、修改common.inc.php

修改include/common.inc.php文件,增加用户是否登录的判断,然后根据用户是否登录,开启memcached缓存。

 

在文件中找到下面这几行

$discuz_auth_key = md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);
list($discuz_pw, $discuz_secques, $discuz_uid) = empty($_DCOOKIE['auth']) ? array('', '', 0) : daddslashes(explode("/t", authcode($_DCOOKIE['auth'], 'DECODE')), 1);

在下面增加下面几行,作用是:据用户是否登录,开启memcached缓存

//缓存
if ($discuz_uid) {//已经登录
 $db->isCache = false; //关闭缓存
}else{
 $db->isCache = true; //未登录用户开启缓存
}

 

转载本文请保留作者信息,违者必究

 

作者:覃学涵(http://www.3sys.cn/)

Email:[email protected]

你可能感兴趣的:(sql,优化,mysql,function,memcached,query)