最近幻想游戏的服务器响应速度极其慢,用户签到可能要等十几秒才能成功。然而也么得钱买更高配置的服务器,只能在程序上想想办法。
分析问题
- 网站负载经常飙到100%,服务器配置很低,cpu是单核的,理论上平均负载在2以内才行,然后负载有时能飚到7。但是cpu、内存还算正常。
- 一般来说,网站大的开销应该是出在数据库上,现在数据库也有十几张表,类似游戏表、用户表、签到表、下载记录表、积分记录表、搜索记录表数据量非常大。有的表数据量已经几十万了,平时都没注意到。
- 网站有各种排行榜数据,实时更新的,都是从各个表读取计算来的,对数据库的读写很频繁。
解决问题
- 首先拿MySQL数据库开刀,像下载记录表,已经60多万条数据了,竟然连索引都没有,只能说自己很粗心啊。实习的时候,经理对我们说,每个表至少都要建立2-3个索引,索引的利远远大于弊。第一步,先给各个表建立索引,初步基本是给where条件查找的字段建立了索引。
-
然后是排行榜,排行榜其实没有实时更新的必要的,于是,上redis。
2.1 用户的积分和签到天数排行榜,直接用redis的有序列表,简单粗暴:public function rankBySignin(){ //如果没放在redis,加进去 if (Redis::zcard('signinRank') == 0) { $list = User::where('isadmin','!==',1)->where('state',1)->orderBy('maxsignins','desc')->orderBy('viplevel','desc')->orderBy('coin','desc')->take(100)->get(); foreach ($list as $k => $v) { Redis::zadd('signinRank',$v->maxsignins,$v->name); } } $rank = Redis::zrevrange('signinRank',0,-1,'WITHSCORES'); return view('web.rank.rankbysignin',compact('rank')); }
2.2 对于游戏的排行榜,涉及的属性比较多,用了redis的List数据类型,将数据转为json存在redis里面。代码比较长,就不贴在这里了。
至此把排行榜数据都缓存到redis了,写了个定时任务,每周更新一下数据。但是上线的时候composer安装predis的时候,被墙的厉害,没有办法,先把这块的代码注释了,等能安装了再上线。 - 将一些无用数据,直接干掉。类似签到表,我们目前的需求是记录用户连续签到天数,以发放积分奖励,理论上,1天前的数据都是可以没用的,所以我直接把一个月之前的数据全删了(其实不喜欢这种方式)。
后续
优化过后,负载状态改善了很多,但是并发过大,还是会扛不住,所以优化工作还得继续。后续准备把需要读取的数据且非实时的,全缓存到redis,将游戏的访问量下载量、访问量放在redis操作。但其实能发现,程序上的优化还是有限的,不过可以提高自己优化项目的能力,去分析问题再解决,感觉还是很爽的。等实在扛不住的时候再去阿里云“打钱打钱”。
更新
优化后,负载是有一定程度的降低。但是偶尔还是会飙到很高,高到一个很不正常的值,最后经确定,是被攻击了。分析一下整个防守攻击的过程——对于网络安全的一些研究和思考