Discuz!官方免费开源建站系统

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索

[已解决] 论坛的INNODB方案

[复制链接]
Luca. 发表于 2013-4-19 15:49:17 | 显示全部楼层 |阅读模式
本帖最后由 xooass 于 2013-6-26 14:23 编辑

论坛INNODB方案的探讨

最新:INNODB方案已出,地址:https://discuz.dismall.com/thread-3330856-1-1.html
本套方案包括一个自动化引擎处理脚本,扩展包,以最小的修改来完成对innodb的支持。
对于post表,采用了缓存分发楼层数的方法来控制楼层编号
并包含一套错误自动修复机制。


大型论坛的现状
discuz!阵营里不乏日PV百万以及过千万级别超级大站,而X系列推出后为了适应这种大站的需求也在底层做了很多改动,比如内存级缓存的加强,分布式部署的支持,内置的分表等,但仍然存在可以优化的空间,比如用服务器cron来代替论坛的模拟计划任务,innodb存储引擎的使用等,这些建议虽然由于目前的一些条件限制不能全面发布,但是我们可以在私下进行探索。

INNODB的使用限制
升级到X2.5后,post表的主键由pid改成(tid,position)的联合主键。




由于新联合主键的作用,在翻页性能上得到极大提升,但是一些先前在X2使用innodb引擎的论坛却发现论坛在X2.5下出现了大量空楼层的情况。



而且由于联合主键的作用,使得用户的楼层在某些情况下可能出现变动,所以抢沙发等插件也不再生效了。

MYISAM的局限

MYISAMdiscuz!论坛的数据库默认引擎,是一种高效简单的解决方案。
不过MYISAM只支持表级锁定,每一个写操作都会锁定整个表,大型论坛尤其是在发起抢楼贴之后很可能出现如下情况。



大量用户同时发帖和读帖,由于写操作优先级大于读操作,post表首先处理写操作,给表上一个写锁。
同时其他大量写操作也在请求,由于表已经锁了,所以把锁定请求写在写锁定队列里。
这时候对于读操作来说,由于表锁定使得许多线程同时从一个表中进行读操作,但线程在操作表的时候必须首先获得独占访问,所以只能把锁请求放到读锁队列。而大量读操作在后面等候表锁定释放,超过脚本时间就会显示错误。或者打开帖子时巨慢。
为了缓解这个问题,discuzX系列在插入新帖子的时候使用了 LOW_PRIORITY关键字来降低写操作的优先级,使得读操作能更顺利。
另外后台内置了分表功能,当一个表数据行过多时可进行分表,能有效降低表锁的概率。


INNODB的爱与恨

理论innodb引擎能实现行级锁。
MYISAM中,假设我的一个表有1KW条数据。然后对某个帖子进行编辑,提交的这一瞬间,MYSQL全表写锁定,这时候发过来的其他任何请求都被阻塞,直到这个写锁定被释放。
而在innodb中,提交的时候只对这一行数据进行锁定。其他不冲突的请求也能同步进行。

         INNODB的伪行锁特性
oracle熟悉的人可能会误认为mysql的行锁也是真正的行级锁,锁定时是对数据行上锁,而事实上不是。Innodb的行锁实质上是基于索引来实现的,所以就有很多注意事项。下面来具体说说这个问题。
1.        Post表中使用如下语句:UPDATEpre_forum_post SET status=1 WHERE pid=123;
这是真正的行级锁,因为pid是唯一键,索引直接定位到这一行。
2.        UPDATE pre_forum_post SETstatus=1 WHERE status=0;
由于status没有索引,所以会导致表锁。
3.        UPDATE pre_forum_post SETstatus=1 WHERE pid >1 AND pid <10;
这儿用到的索引类型是range,所以pid2~9的行数被锁,如果这时候有另外一个请求更改pid=5的行,也会被放入到写锁队列中。
4.        UPDATE pre_forum_post SETstatus=1 WHERE invisible=1 AND pid=1;
如果有一个invisible索引,如果这时候有另外一个语句:
UPDATE pre_forum_post SET status=1 WHERE invisible=1 ANDpid=2;
虽然这两个不指向同一行,但是由于第一句中使用了invisible的所以,所以会导致所有invisible=1的行数全部被锁,所以第二句也不能同时进行操作。
所以由此可见,锁行的精细度是和索引使用的精细度相同的。这一点在使用的时候必须注意了。
INNODB的其他问题
1.      占用更多内存。
2.      锁定速度更慢。
3.      SELECT COUNT(*)统计全表时需要遍历所有行。
4.      GROUP BY操作效率低下。
INNODB的适用范围
是一个值得尝试的方案,尤其是对于写操作比较频繁的论坛,比如手机论坛这种需要靠回帖挣积分的论坛。不过对于一般小站来说,意义并不是很大。
如果你的论坛有如下情形,建议使用MYISAM
1.      主要用于展示内容的。
2.      主要用于展示内容和INSERT操作。UPDATE和DELETE操作很少。
3.      站点不大,日PV小于10W。


DISCUZ!论坛的INNODB使用方案

代码修改
代码中主要是针对SELECT COUNT(*)全表搜索的时候的优化。所涉及的文件均在source/class/table文件夹内
首先需要在class/discuz/discus_database.php中增加一个count_all的方法,修改如下:
339行插入如下代码:
  1. public static function count_all($$$$table) {
  2.                 $$$$linkId = 1;
  3.                 if(!empty(self::$$$$db->config['map']) && !empty(self::$$$$db->config['map'][$$$$table])){
  4.                         $$$$linkId = self::$$$$db->config['map'][$$$$table];
  5.                 }
  6.                 $$$$dbname = self::$$$$db->config[$$$$linkId]['dbname'];
  7.                  $$$$table = self::$$$$db->table_name($$$$table);

  8.                 self::$$$$db->select_db('information_schema');
  9.                 $$$$count = self::$$$$db->result_first("SELECT TABLE_ROWS FROM `TABLES` WHERE TABLE_SCHEMA = '$$$$dbname' AND TABLE_NAME = '$$$$table'");
  10.                 self::$$$$db->select_db($$$$dbname);
  11.                 return $$$$count;
复制代码

X2.5文件修改列表
   
文件名
  
行数
主要修改
Common_block_permission
55
替换为:
   
return $where ? DB::result_first('SELECT  COUNT(*) FROM '.DB::table($this->_table).$where) :  DB::count_all($this->_table);
Common_block_style
34
替换为:
   
return $wheresql ?  DB::result_first('SELECT COUNT(*) FROM  '.DB::table($this->_table).$wheresql) : DB::count_all($this->_table);
Common_credit_log
120
替换为:
   
return $condition[0] ?  DB::result_first('SELECT COUNT(*) FROM %t '.$condition[0], $condition[1]) :  DB::count_all($this->_table);
Common_diy_data
51
替换为:
   
return $wheresql ?  DB::result_first('SELECT COUNT(*) FROM  '.DB::table($this->_table).$wheresql) : DB::count_all($this->_table);
Common_invite
63
替换为:
   
return $condition[0] ?  DB::result_first('SELECT COUNT(*) FROM %t '.$condition[0], $condition[1]) :  DB::count_all($this->_table);
Common_member
170,174,350,354
分别替换为:
   
$count = DB::count_all($this->_table);
   
$count +=  intval(DB::count_all('common_connect_guest'));
   
if(!DB::fetch_first('SELECT * FROM  '.$temptablename.' LIMIT 1')) {
   
if(DB::fetch_first('SELECT * FROM  '.$temptablename.' LIMIT 1')) {
Common_member_archive
48
替换为:
   
return isset($this->membersplit) ?  DB::count_all($this->_table) : 0;
Common_member_verify_info
49
替换为:
   
return $condition[0] ?  DB::result_first('SELECT COUNT(*) FROM %t '.$condition[0], $condition[1]) :  DB::count_all($this->_table);
Forum_attachtype
51
替换为:
   
return $wheresql ?  DB::result_first("SELECT COUNT(*) FROM %t $wheresql", $parameter) :  DB::count_all($this->_table);
Forum_grouplevel
27
替换为:
   
return DB::count_all($this->_table);
Forum_medallog
81
替换为:
   
return $where ?  DB::result_first("SELECT COUNT(*) FROM  ".DB::table('forum_medallog')." $where") :  DB::count_all($this->_table);
Forum_thread
982,1023,1028
分别替换为:
   
return $wheresql ?  DB::result_first("SELECT COUNT(*) FROM %t $wheresql", $parameter) :  DB::count_all($this->_table);
   
return DB::count_all($this->get_table_name($tableid));
   
return $condition[0] ?  DB::result_first('SELECT COUNT(*) FROM %t '.$condition[0], $condition[1]) :  DB::count_all($this->get_table_name());
Home_blacklist
37
替换为:
   
return $wheresql ?  DB::result_first("SELECT COUNT(*) FROM %t $wheresql", $parameter) :  DB::count_all($this->_table);
Home_favorite
56
替换为:
   
return $wheresql ?  DB::result_first("SELECT COUNT(*) FROM %t $wheresql ", $parameter)  : DB::count_all($this->_table);
Home_follow
100,112
分别替换为:
   
$count = $wheresql ? DB::result_first("SELECT  COUNT(*) FROM %t $wheresql", $parameter) :  DB::count_all($this->_table);
   
$count = $wheresql ?  DB::result_first("SELECT COUNT(*) FROM %t $wheresql", $parameter) :  DB::count_all($this->_table);
Home_follow_feed
76
替换为:
   
$count = $wheresql ? DB::result_first("SELECT  COUNT(*) FROM %t $wheresql", $parameter) :  DB::count_all($this->_table);
Home_friend
104
替换为:
   
return $wheresql ?  DB::result_first('SELECT COUNT(*) FROM %t '.$wheresql, $parameter) :  DB::count_all($this->_table);
Home_share
137,158,195
分别替换为:
   
return $wheresql ?  DB::result_first('SELECT COUNT(*) FROM %t '.$wheresql, $parameter) :  DB::count_all($this->_table);
   
return $wheresql ?  DB::result_first('SELECT COUNT(*) FROM %t '.$wheresql, $parameter) :  DB::count_all($this->_table);
   
return $wheresql ?  DB::result_first('SELECT COUNT(*) FROM %t '.$wheresql, $parameter) :  DB::count_all($this->_table);
Portal_topic
25,26
替换为:
   
$wheresql = empty($wherearr) ? '' : '  WHERE '.implode(' AND ', $wherearr);
   
                   return  $wheresql ? DB::result_first('SELECT COUNT(*) FROM  '.DB::table($this->_table).$wheresql) : DB::count_all($this->_table);
Portal_category_permission
54
替换为:
   
return $where ? DB::result_first('SELECT  COUNT(*) FROM '.DB::table($this->_table).$where) :  DB::count_all($this->_table);
注:table目录修改的文件列表如附件:

数据库修改:修改除postsession表之外的所有表引擎为INNODB
由于post的联合主键设置,目前暂没有更好的方法。对于这个问题,希望大家能一起提出自己的意见,如果有好的观点也可以通过站内信获取我的联系方式。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

评分

3

查看全部评分

小柒iphone 发表于 2013-4-19 16:12:36 | 显示全部楼层
沙发我的
回复

使用道具 举报

愤怒的叼丝 发表于 2013-4-19 16:13:03 | 显示全部楼层
太长了撒
回复

使用道具 举报

baxter 发表于 2013-4-19 16:17:17 | 显示全部楼层
{:soso_e100:}{:soso_e179:}
回复

使用道具 举报

vivafei 发表于 2013-4-19 16:22:41 | 显示全部楼层
其实应该用DZ官方目前的架设为例给大家做个解说估计会更有实际性
回复

使用道具 举报

xiaoyuwxz 发表于 2013-4-19 16:43:15 | 显示全部楼层
学习了,就是啊,官方也不出个系统的教程或方案
回复

使用道具 举报

crx349 发表于 2013-4-19 17:37:21 | 显示全部楼层
不错 支持下 送你一分
回复

使用道具 举报

myp4p 发表于 2013-5-2 19:09:44 | 显示全部楼层
本帖最后由 myp4p 于 2013-5-2 22:26 编辑

class/discuz/discus_database.php的插入代码最后少了个}
回复

使用道具 举报

myp4p 发表于 2013-5-3 11:26:21 | 显示全部楼层
使用联合主键的不止post表吧,好像还有一个common_member_grouppm的表

评分

1

查看全部评分

回复

使用道具 举报

火舞狂龙 发表于 2013-5-3 11:53:18 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|Discuz! 官方站 ( 皖ICP备16010102号 )star

GMT+8, 2024-11-16 13:39 , Processed in 0.052159 second(s), 5 queries , Gzip On, Redis On.

Powered by Discuz! X3.4

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表