转载本文请保留作者信息,违者必究
作者:覃学涵(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文件,作者这里贴出这个类修改后的完整代码。
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."
/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."
/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]