discuz!x2主题分表、帖子分表功能分析
对于大型站点,庞大的主题和帖子数据,分表放到一个主题表和一个帖子表中,已经成为影响性能的一个因素。因此,急需进行分表操作来避免 MySQL 对于大数据表的频繁操作。
1、后台分表
Disucz! X2 后台重新调整了分表的机制,使得分表效率和后期站点性能得到提升。具体分表设置在 后台 -> 站长 -> 主题分表(帖子分表)。
1)主题分表
主题分表分为两种类型,一种为主表,一种为存档表。主表只有一个,存档表可以有多个。我们进行分表操作,首先进行创建存档表的操作,然后进行主题移动,将指定条件的主题移动到存档表中。
a、创建存档表
打开 source/admincp/admincp_threadsplit.php 文件,找到创建存档表的条件分支
1 |
elseif($operation == 'addnewtable') |
首先获取当前最大的主题表 id ,然后根据 forum_thread 的建表语句,生成新的存档表(id + 1),最后更新缓存。
b、主题移动
存档表建好后,下一步就是将主题移动到这个存档表中。在选择主题的时候,可以根据提供的搜索条件,特别是“更多选项”中的搜索条件,来转移符合条件的主题。这里主要是一个搜索的过程,转移数据的过程使用 REPLACE INTO … SELECT…FROM… 语句,比较简单。
2)帖子分表
帖子分表也是要分成两步,第一步先创建帖子分表,第二步转移数据。
打开 source/admincp/admincp_postsplit.php 文件,找到帖子分表的条件分支
1 |
elseif($operation == 'split') |
如果是对主表进行分表操作,主表必须要大于400M,见下面的条件判断
1 |
if($status && ((!$tableid && $status['Data_length'] > 400 * 1048576) || ($tableid && $status['Data_length']))) |
在站长提交分表表单后,程序会先根据 getmaxposttableid 函数获取当前帖子表最大的 id ,然后根据已有的帖子表结构,创建新分表,并更新缓存等信息。创建成功后,开始转移数据的操作。找到条件分支
1 |
elseif($operation == 'movepost') |
,开始转移数据的操作。当从主表中转移数据时,是根据主题表中最后回复时间正序排列的主题 tid 来获取帖子数据的。
1 |
$query = DB::query("SELECT tid FROM ".DB::table($table)." WHERE posttableid='0' AND displayorder>='0' ORDER BY lastpost LIMIT 0, 1000"); |
然后,利用 movedate 函数进行转移数据。
当从从表中转移数据时,是根据给定帖子表的 id,获取此表中first=1的帖子所属 tid,
1 |
$query = DB::query("SELECT tid FROM ".DB::table(getposttable($fromtableid))." WHERE `first`='1' LIMIT 0, 1000") |
然后利用 movedate 函数进行转移数据。
在 movedate 函数中,根据获取到的 tid ,将属于此 tid 的帖子,利用 INSERT INTO … SELECT … FROM …来转移数据。在此过程中,更新主题表中 posttableid 这个字段,来标识帖子的存储表。
2、主题列表页分表读取
在主题列表页,只能看到主题主表中的主题。存档表中的主题都在存档区。
在这部分的代码(source/module/forum/forum_forumdisplay.php)中,所有涉及到的主题表都以 $threadtable 这个变量代替,$threadtable 这个变量的获取,又是根据用户是否查看存档区的标识 archiveid 来判定。
1 |
$threadtable = $_G['gp_archiveid'] && in_array($_G['gp_archiveid'], $threadtableids) ? "forum_thread_{$_G['gp_archiveid']}" : 'forum_thread'; |
当用户选择非存档内容时,archiveid = 0 或者不存在,这时,直接从 forum_thread 主表来读取主题列表。当用户选择存档内容时,archiveid = 1(1 表示存档表的 id,如果查看 id 为 2 的存档表,archiveid = 2),程序会根据 archiveid 来判断从哪个存档表中去获取主题列表。
3、帖子内容页分表读取
当用户浏览帖子内容页时,程序首先利用 loadforum 函数来获取当前的主题信息。在 loadforum 函数中,
1 |
$_G['thread'] = get_thread_by_tid($tid, '*', $addcondiction, $archiveid); |
根据提供的 tid,get_thread_by_tid 函数到主表和存档表中分表去查找,直到找到对应的主题信息。
在 source/module/forum/forum_viewthread.php 文件中,根据
1 |
$thread = & $_G['forum_thread']; |
1 2 3 |
$threadtable = $thread['threadtable']; $posttableid = $thread['posttableid']; $posttable = $thread['posttable']; |
这两句来判定该主题应该去哪个帖子表中去获取帖子内容列表。
4、发新主题
发表新主题时,直接往主题主表中插入数据,对应的 posttableid = 0。然后,当插入帖子数据时,通过 insertpost 函数将数据插入帖子表中。
在 insertpost 函数中,会先去判断所属主题的 posttableid 值,得到目标帖子表,然后再往该帖子表中插入帖子数据。
5、发新回复
只有在主表的主题才能回复,因此,直接根据传递的tid去获取目标帖子表,然后插入数据。这个跟发新主题时,插入帖子内容过程相同。
6、编辑帖子
同样,只有在主表中的主题的帖子才能被编辑,因此,主题都在 forum_thread 表中。需要获取的是该主题的帖子在哪个帖子表中。打开 source/include/post/post_editpost.php 文件,找到
1 |
$posttable = getposttablebytid($_G['tid']); |
程序在开始,通过这条语句来获取目标帖子表。在 getposttablebytid 函数中,程序根据 tid ,逐个主题表进行查找,找到对应的主题信息,获取目标帖子表,然后指向后续的编辑帖子操作。
8、取消存档
存在存档区的主题,如果想取消存档,可以通过页面底部的“取消存档”来执行。
打开 source/include/topicadmin/topicadmin_restore.php 文件,可以看到取消存档,实际上仅是将放在存档表中的主题移动到了主表中。同样,存档表的获取还是通过 archiveid 这个参数来决定的。
1 |
$threadtable = $archiveid ? "forum_thread_$archiveid" : 'forum_thread'; |
从这可以看出,存档的概念仅限于主题,帖子无所谓存档的概念。