下午有同事反映之前升级的两个站点开通了QQ互联后出现缓存丢失问题,有一个站点排查发现是由于QQ昵称的特殊字符导致,整理排查过程。
问题描述:
使用QQ登录注册后(QQ昵称带有特殊字符,假设为),论坛缓存出现丢失情况,手动更新后恢复正常,丢失情况类似下图:
[caption id="attachment_971" align="alignnone" width="300"] Discuz! 缓存丢失后的页面[/caption]
排查分析:
首先看前台显示,发现注册的“精€€ぷ灵€€”用户名显示成了“精”,进入数据库里的pre_common_member表中确定当前注册的用户名是什么,如图:
[caption id="attachment_972" align="alignnone" width="300"] 用户表记录[/caption]
从图可以看出来,注册的“精€€ぷ灵€€”用户名到数据库里变成了“精”,推测是由于Mysql把特殊字符后面的所有东西全部干掉导致。
注册用户时会更新缓存表里的userstats记录,进入pre_common_syscache表里找到对应记录,如图:
[caption id="attachment_973" align="alignnone" width="300"] 缓存表记录[/caption]
从记录可以看出来,这里的序列化结果断掉了,同样的也是从特殊字符开始后面的所有东西都没有了。
至于为什么手动更新缓存就会好,是由于手动更新缓存的时候,最后注册会员的名字是从用户表中查询出来然后再序列化写入缓存表,用户表中的用户名是没有特殊字符的,所以写入缓存表的数据结构是完整的,所以缓存没有问题,有兴趣的童鞋可以参考source\function\cache\cache_userstats.php文件代码。
原因猜测:
个人猜测是由于Mysql不支持特殊字符,插入的带有特殊字符数据,Mysql会将特殊字符及后面的数据全部扔掉。
由于用户名中带有特殊字符,所以缓存表里的记录的序列化值中也会含有特殊字符,同样的抛弃特殊字符及后面的数据,就会导致序列化结构不完整,进而导致前台显示的时候由于反序列化失败导致缓存不完整。
解决方法:
修复普通注册:找到source\module\member\member_register.php文件,搜索下面的代码:
$totalmembers = DB::result_first("SELECT COUNT(*) FROM ".DB::table('common_member'));
$userstats = array('totalmembers' => $totalmembers, 'newsetuser' => $username);
save_syscache('userstats', $userstats);
改为:
require_once libfile('cache/userstats', 'function');
build_cache_userstats();
修复QQ互联注册(仅针对Discuz! X1.5的QQ互联插件):找到source\module\connect\connect_register.php文件,搜索代码:
$totalmembers = DB::result_first("SELECT COUNT(*) FROM ".DB::table('common_member'));
$userstats = array('totalmembers' => $totalmembers, 'newsetuser' => $username);
save_syscache('userstats', $userstats);
改为:
require_once libfile('cache/userstats', 'function');
build_cache_userstats();
搜索代码:
$_G['setting']['lastmember'] = $username;
save_syscache('setting', $_G['setting']);
删除这段代码。
以上的方法可以解决缓存丢失的问题,但是根本的解决方法应该是在用户注册时过滤特殊字符。