大数据量查询优化

问题描述:涉及到大数据量,多循环查询的时候,往往查询的速度会变慢,影响系统的使用性能。该问题,在测试环境尚不明显,因为测试环境的数据量毕竟是有限的。

但是,一旦将代码更新到线上的真实系统,因为数据量一下子增大,会造成数据查询的缓慢,所造成的严重迟滞,就不能被忽略了。

业务场景:云计算系统。底层会将采集过来的宿主机流量数据,进行保存。后台管理系统,需要定时计算从底层传递过来的宿主机的带宽的总体实际使用量,并且用实际使用量,除以节点的总带宽,得到相关的出网、入网的带宽使用率。


方案分析:
1.在保存数据的时候,事实计算出数据使用量,后续查询时,通过sql语句的sum方法直接汇总。
2.开启多线程并发查询。


一、利用Callable多线程,实现查询优化的例子

原理同一个Callable的简单例子。核心在于运用多线程,创建带有返回值的查询。

1.准备查询参数

2.根据每页数据量,去判断应该创建的线程的数量。

3.根据数据的总数量,平均分配每个线程处理的数量。

4.将查询返回的Future,保存在list中(注意,这样做可以提升查询速度,先批量保存结果,再处理的方式,要快于拿到值就处理的方式)

5.        latch.await();

6.遍历List,数据处理+分页,返回数据值。

public String services(){
		List <Future<Map<String,Object>>> list=new ArrayList<Future<Map<String,Object>>>();
		int total=0;
		//1.准备需要传递的参数
		String condition = dealContent();
		List<CompanyEntity> companyList = companyService.list(condition, -1, -1);
		List<Node> nodeList = new ArrayList<Node>();
		int nodeId = getNodeId();
			if(nodeId != 0){
				Node node = nodeService.getById(nodeId);
				nodeList.add(node);
			}else{
				nodeList = nodeService.getAll();
			}
		List<JSONObject> comList = new ArrayList<JSONObject>();
			for(CompanyEntity company : companyList){
				for(Node node : nodeList){
					JSONObject jsonObj = new JSONObject();
					jsonObj.put("companyName", company.getCompanyName());
					jsonObj.put("companyId", company.getCompanyId());
					jsonObj.put("companyType", company.getType());
					jsonObj.put("nodeName",node.getNode_name());
					jsonObj.put("nodeId",node.getNode_id());
					jsonObj.put("nodeIp", node.getNode_ip());
					comList.add(jsonObj);
				}
			}
		//2.数据分页处理
		int pagesize=Integer.parseInt(super.getPagesize());
		int nopage=Integer.parseInt(super.getNowpage());
		int ThrearNum=0;
		if(pagesize>ThreadUtil.CORE_POOL_SIZE){
			ThrearNum=ThreadUtil.CORE_POOL_SIZE;
		}else{
			ThrearNum=pagesize;
		}
		try {
			//计算数据总量:
			int sumDataCount=comList.size();
			List<JSONObject> dataList=new ArrayList<JSONObject>();
			
			final CountDownLatch latch = new CountDownLatch(ThrearNum);
			boolean flag=false;
			int dataEnd=0;
			int addData=0;
			int j=1;
			for (int i = 0; i < ThrearNum; i++) {
				List<JSONObject> addList=new ArrayList<JSONObject>();
				List<JSONObject> tempList=new ArrayList<JSONObject>(comList.size());
				tempList=comList;
				List<JSONObject> dealList=new ArrayList<JSONObject>();
				
				if(sumDataCount%ThrearNum!=0){
					addData=sumDataCount%ThrearNum;
					flag=true;
				}
				//计算每个线程要处理的数据总量:
				int dataCountPerThread=sumDataCount/ThrearNum;
				//计算当前线程数据处理的起始索引:
				int dataStart=i*dataCountPerThread;
				//计算当前线程数据处理的结束索引:
				dataEnd=(i+1)*dataCountPerThread;
				//截取List,作为当前线程所需要处理的全部数据:
				if(flag){
					if(j<=addData){
						//多余的数据,从最后的索引开始平均分配
						addList.add(tempList.get(tempList.size()-j));
						j++;
					}
					dealList=subData(tempList,dataStart, dataEnd);
					if(addList.size()!=0){
						dealList.add(addList.get(0));
						addList.remove(0);
					}
				}else{
					dealList=subData(tempList,dataStart, dataEnd);
					//如果这里不使用subData,而是使用subList,则会报错:java.util.ConcurrentModificationException
					//dealList=comList.subList(dataStart, dataEnd);
				}
				
				Callable<Map<String,Object>> c1 = new CallableCountThread(condition,dealList,VpcFlag,slbFlag,blockFlag,vpnFlag,latch);
				Future<Map<String,Object>> f1=pool.submit(c1);
				list.add(f1);
			}
			
			latch.await();
			for(int i=0;i<list.size();i++){
				Future<Map<String,Object>> f1=list.get(i);
					total+=(Integer)f1.get().get("count");
					if(null!=f1.get().get("data")){
						dataList.addAll((List<JSONObject>)f1.get().get("data"));
					}
					
			}
			doPage_special(total);
			//3.查询分页开始和结束的索引位
			int  nowpage2 = (Integer) dataMap.get("nowpage"); 
			int  pagesize2 = (Integer) dataMap.get("pagesize");
			
			List<JSONObject> showData=new ArrayList<JSONObject>();
			if(dataList.size()==0){
				
			}else{
				if(nopage==1){
					if(dataList.size()<=pagesize2){
						showData=dataList.subList(0,dataList.size()-1);
						showData.add(dataList.get(dataList.size()-1));
					}else{
						showData=dataList.subList(0,pagesize2);
					}
				}else{
					if(dataList.size()<=(nopage)*pagesize2){
						showData=dataList.subList((nopage-1)*pagesize2,dataList.size()-1);
						showData.add(dataList.get(dataList.size()-1));
					}else{
						showData=dataList.subList((nopage-1)*pagesize2,nopage*pagesize2);
					}
				}
			}
			generateSuccessListResponse(showData);
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return JSON;
	}

二、查询效率

没有使用duox多线程:

 

查询时间在16s左右

 

使用多线程查询:

查询时间在1.7s左右

 

查询速度提升了十倍。

 

三、心得总结:

1.写代码的时候,不能仅仅只考虑功能的实现。特别是涉及到数据量比较大的时候,任何一次查询的精简,带来的效率提升都是显著的。

2.多线程在具体场景中的使用是非常重要的。在涉及到返回值的时候,就需要使用到Callable接口。

3.任何时候,写完代码,都要自己给自己做code review。回过头去看看自己写的代码。能不能优化,能不能再优化。

 

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值