discuz7.2针对session表的分析和优化
07-7
广告时间:新建一个qq群,欢迎大家进来讨论discuz的性能优化 qq群 41886598 接头暗号:dz
引子
最近一个朋友的论坛由discuz转化为phpwind,因为discuz在负载较大的情况下频频当机,但在我观察,瓶颈基本是数据库,并且负载最高的表定位为sessions表。之前我们论坛在进行了大的改进后,除了分表这些基本优化,改动最大的就是sessions表。我们使用张宴写的tcsql来存储用户的session信息,经推算,系统负载应该能支撑10~20w人同时在线。
这个10~20w人只是推测,因为原来我们优化sessions表但没有切换到tcsql的时候,最高支撑到4~6w人-也就是某次游戏临时维护,用户大量涌向论坛,在大概4~6w用户的情况下,论坛崩溃。而tcsq的读写效率远远高于mysql,因此推算tcsql代替sessions表的情况下,论坛至少可以承载10~20w人同时在线。
毕竟tcsql用C进行开发,而且没有开源。去年的文章发表后,一直有朋友在探讨dz的优化问题,但那几个方案都依赖了第三方的应用。这次,我想到的是在不依赖其他应用的前提下,完成dz的sessions表优化。
这里,我对sessions表的访问情况作了一个统计:
一个没访问过的论坛,我作为一个用户:1、进入首页 2、ajax登录 3、进入某板块 4、点击某帖子。4个操作,统计session表的操作:select 5次,insert 2次,delete 2次,update,3次,select联查,4次,一共16次操作。对于一个成熟的网站,大部分用户的操作连起来,统计数据跟我统计的会有区别,但从全局看来,sessions表的确是mysql查询数的一个瓶颈。
以子之矛
对于这个问题,dz程序其实是有解决方案的。
- if($pvfrequence && $discuz_uid) {
- if($spageviews >= $pvfrequence) {
- $pageviewsadd = ', pageviews=\'0\'';
- $db->query("UPDATE {$tablepre}members SET pageviews=pageviews+'$spageviews' WHERE uid='$discuz_uid'", 'UNBUFFERED');
- } else {
- $pageviewsadd = ', pageviews=pageviews+1';
- }
- } else {
- $pageviewsadd = '';
- }
这个功能是统计用户浏览量的一个小东西,$spageviews是保存在用户session表的一个变量,如果每次浏览都更新用户表的浏览数,member表的负担是很大的。dz的程序员就用客户端更新数据这种方式,把数据更新动作放在了session表,每$spageviews个周期更新一次,有效降低了member表压力,而且此配置在网站后台作出了用户接口。
为什么这个问题可以解决,只不过把压力进行了转移,而sessions表的压力为什么不能解决一下呢?我百思不得其解。
攻子之盾
member的压力可以转移到sessions表,当然sessions表的压力就不能再转移给别的表了,这样处理,不管转移到哪里,都是数据库的压力,所以,得想办法转移到数据库之外。
再考虑为什么member的压力可以转移:因为用户浏览数这个统计项,用户每必要也不需要实时看到更新。
而用户的session信息是用户实时要用,并且有部分权限控制方面的东西,是非常必要的,所以,必须实时获取。既然其他表的压力也都来了sessions表,那更新也必然需要在sessions表中做。这个事情,不就是php的session应该做的事情么?
再考虑sessions表除模拟php的session功能外的其他功能:
1、状态传递:需要把用户的当前状态实时传递给其他用户。在线状态、用户所在位置等。
2、统计分析:在dz可以看到当前多少用户在线,对于单纯使用php session功能是无法满足的,故dz采用sessions表来替代这个功能。
现在就设计一个机制,既满足用户session的功能,又满足统计分析的功能。当然,用php+mysql了。需要及时更新的信息存储于php的session,而需要统计的信息分阶段更新到mysql。
流程如此 用户创建一个mysql记录,创建一个session,session的过期机制类似于mysql表。每次访问,取session信息,如果session信息不存在,取sessions表中的信息。像用户浏览次数这类数据,可以每过一段时间更新到mysql中。用户session每次更新,也只是更新session数据,而session机制的并发性和效率,远远高于mysql的。如此解决,既不阉割原有的功能,又不会对环境造成很严重的依赖,只要支持phpsession的环境即可。
总结
在修改完成后,我又进行了测试,这次session表的访问频度已经降到了原来的1/3之下,我想,对于大型的论坛,起码能节约1/3的服务器吧。这样改动也并不是没有缺点的:统计就会有延迟和偏差。因为中间用户突然离开,会损失sessions表更新的及时性甚至会损失这部分数据。如果严重依赖论坛自己某些统计功能的话,那这种方式欠妥,但对于已经使用第三方统计或者对那部分数据不很敏感的论坛用户,这种方案还是非常适合的。
附言:dz的大多数产品设计都采用sessions表这种设计思路,因此,这个思路可以优化几乎所有的dz产品。
tips:如何定位sessions表的负载: 在mysql数据类的query方法里增加如下代码,数据少可以用肉眼数,数据多可以写个脚本去分析
- $handle = fopen("sql.log","a+");
- fwrite($handle,str_replace("\r\n","",$sql)."\r\n");
- fclose($handle);
附上代码: