测试:有个查千万级的页面,查的特别慢导致页面都崩溃了,你们开发解决一下。
开发组:sql我们已经做优化了,它还是慢我们也没办法啊,优化我们都不会啊,新来的,要不你看一下?只要保证页面不崩就行。
我:嗯?好吧,刚来也没事干,就看一下吧。
关于sql优化学习尚浅,只知道降低查询结果集的数据量来解决,比如分库分表、建索引、分页查询,还有最近刚学习的Oracle强大的分区表!同事说保证页面不会崩溃就行了。。。呃,好的。
只考虑IE和谷歌浏览器,IE没有页面无响应时的处理机制,而谷歌会在页面很长时间无响应后,就会触发,弹出“页面已经崩溃了”的提示。
分析了一下页面崩溃的原因:
由于查询是同步的,会阻塞主线程,导致系统所有页面都处于“不可响应”的状态。
了解了原因后,可以采用多线程和异步的方式来解决:
1、改成异步查询
2、采取多线程查询(需要考虑线程相关问题,比如:死锁、线程安全等)
异步比出现很多不必要的问题。
异步不很简单么,ajax不就异步的么!
由于技术太古老以及专属框架,ajax无法应用上。。。网上说js的setTimeout函数就是异步的,然后我就将查询方法放到了该函数里,测试结果告诉我不行!!!
思考一下:异步和同步、阻塞和非阻塞,排列组合的4种情况。
(知识尚浅,有不正确的情指正)
感觉异步也是一个主线程,可能会阻塞,也可能不阻塞。像一个网页中的元素图片、文字之类,页面加载完成之后,图片才会慢慢的异步加载出来,且不会阻塞用户的响应操作。这和H5的标签特性好像有关系,img标签的src属性好像就是异步加载的。
异步好像行不通,那就研究多线程吧。
直接上代码,详细的用法讲解请参照Web Worker 使用教程
1、第一步创建子线程的commonWorker.js文件
//引入外部大佬封装好的查询组件js
importScripts('../common/EasyQueryVarxxx.js');
//监听主线程传来的参数
this.addEventListener('message',function(event){
var sql=event.data;
var resultMsg=easyQueryVerxxx(sql); //大佬封装的公共查询函数,传入sql语句,直接返回结果
//把结果返回给主线程
this.postMessage(resultMsg);
console.log('worker query end');
self.close();
},false);
2、在正常js文件中改变查询方式,通过commonWorker.js文件来查询结果
//开启子线程进行处理
var worker = new Worker("./commonWorker.js");
//向子线程传递参数
worker.postMessage("select xxx,xxx,xx, from t_xxxx");
//监听子线程发送回来的数据(回调函数)
worker.onmessage = function (event){
var resultMsg=event.data;
//已经在主线程中拿到结果了,然后正常处理就可以额
console.log(resultMsg);
};
这是最简单的代码,要保证系统的可靠性,还需要做控制。假定子线程的查询需要2s的处理时间,由于后台发生了意外的情况,导致子线程2s后还没有返回,然后用户改变的查询条件又点击了一次,这时又一个线程去后台查询了,但这次没有意外,很快用户看到了结果,但由于上一个子线程正常返回了,用户就会看到结果后又被刷新为上一次的查询结果,或者其它未知的情况。
我负责的模块由于性能需要,根据查询条件来决定是否开启子线程去查询。
所以对代码稍作改造,加入一个共享变量来同步,就可以了。
加入共享变量
//定义一个全局的同步变量
var synchroFlag=0;
--------------
//每开启子线程去处理时
synchroFlag=1;
var worker = new Worker("./commonWorker.js");
worker.postMessage("select xxx,xxx,xx, from t_xxxx");
worker.onmessage = function (event){
var resultMsg=event.data;
console.log(resultMsg);
//释放
synchroFlag=1;
};
--------------
//在查询中添加判断
if(synchroFlag>0){
alert("请稍等上一次查询结束后,再查询。");
return false;
}
这样,就可以基本满足业务需要了。