声明
本文为Gleasy原创文章,转载请指明引自Gleasy团队博客
问题初级定位方法: 一感三看
一感,凭个人感觉,操作不流畅,有挫折感肯定有问题。
三看:
一看ajax请求的执行时间,网络条件好的情况下,超过400MS的肯定有问题;
二看静态内容(html,图片,js,css)等是否cache,没有cache肯定有问题;
三看同一个ajax请求的数量,如果连续有N个(N>3)以上同一个AJAX请求,肯定有问题;
问题深入定位方法
方法一:使用stopwatch,在程序中植入log记录执行时间,通过查看执行时间以定位出问题的代码段;
方法二:间隔重复可疑操作,观察记录CPU变化情况。用以观察是否有死循环或者大规模遍历。
方法三:恶意快速重复点击同一个功能按键20次,观察是否会重复发起后端请求20次,如果是,那么,也是有问题的。
方法四:使用SQL监控(druid)来观察每一个SQL执行次数,执行时间,从而发现热点问题
应用技巧
NO1. 弱化数据库
1.将数据库弱化为“存储”,避免使用数据库查询能力,尽量使用主键或唯一键进行精确读取动作,数据量不太大的表可以使用普通索引进行精确读取,避免使用范围查询(> < != between in),禁止使用like,禁止使用子查询,联合查询,exists查询;
2.范围查询和like查询,一律使用索引平台CloudIndex替代;
3.子查询,联合查询,exists查询:一律在应用层进行逻辑拼装(如果数据量太大,可以使用Map-Reduce进行多线程计算,如果再大可以使用cloudjob进行分布式调度)
NO2. 善用缓存
1.提高缓存命中率为终极目标;
2.容易忽视的缓存问题:大量访问己被删除数据,由于数据不存在,缓存肯定不命中,导致频频访问数据库;解决方法是对己删除的数据做特殊缓存标记;
3.对写入性能要求极为苛刻的场景可以使用redis缓存-存储切换的方式进行异步写入:写入redis(标记为存储),立刻返回,另起独立线程将写入数据同步至数据库,同步成功之后,将redis中相应数据标记为缓存。
NO3. 用好中间件
1. 对于写入性能要求苛刻(或写入并发量特别大的)且允许写入延迟的情况,使用CloudMQ中间件;比如发邮件,发微博,发留言等;
2. 定时任务(比如定时发送邮件,定时提醒),特别适合使用CloudJob中间件;
3. 对于消耗性任务(比如执行时间长且任务量大,消耗CPU资源),可以使用CloudJob进行分布式任务调度,将众多大任务放到N台后台机器上执行;
NO4. 批量操作(从前端到后端)
1. 前端批量,针对大量重复调用某一接口的情况,由于AJAX的异步性,可以将N个请求合并成一个请求,串行执行AJAX,策略如下(伪代码):
01 | var getDepartmentLinkByUid = function(uid,callback){ |
02 | if(loading){cache.push({uid:uid,callbackup:callback}); return;} |
03 | loading = true; |
04 | realGet(); |
05 | } |
06 |
07 | var realGet = function(){ |
08 | var tmp = cache.splice(0); |
09 | $.ajax({ |
10 | data:由tmp的uid拼接而成, |
11 | success:function(dt){ |
12 | for(var i=0;i<tmp.length;i++){ |
13 | var uid = tmp[i].uid; |
14 | var rdata = dd[tt]; |
15 | tmp[i].callback(rdata); |
16 | } |
17 | if(cache.length>0) realGet(); |
18 | } |
19 | }); |
20 | } |
2. 缓存批量(承接上面的例子)
1 | objects = mget(uids); |
2 | notcached = new ArrayList(); |
3 | for(uid:uids){ |
4 | if(objects 不包含uid) notcached.add(uid); |
5 | } |
3. 数据库批量查询(承接上面的例子),批量更新缓存
1 | notcachedData = select * from department where uid in (notcached列表); |
2 | mset(notcachedData 生成的 map); |