zoukankan      html  css  js  c++  java
  • 大批量数据读写

    需求

    大约200W条数据,批量从mysql中读取,然后根据主键再从hbase读数据进行关联,最后再update到数据库中

    同步解决方案

    同步解决方案,也是最接近人脑思考顺序的方案是,分页mysql读取id集合,每页1k条数据,然后拿着idList批量从nosql的hbase中进行数据的获取,进行数据的封装,然后逐条更新到数据库中。实验结果表明,如果要完成这项工作,估计要10小时以上。

    先做个简单的优化,尽可能的降低io开销,将逐条更新回数据库修改成,延迟批量提交数据库。这样1k次io开销缩减成1次。利用ibatis的批量提交特性,具体代码如下

    @Override
    public void batchUpdate(final List<T> objectList) {
    	this.getSqlMapClientTemplate().execute(new SqlMapClientCallback() {
    		public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
    			executor.startBatch();
    			for (T tmp : objectList) {
    				executor.update(sqlmapNamespace + ".update" + , tmp);
    			}
    			return executor.executeBatch();
    		}
    	});
    }
    

    这样再次实验后,发现跟新1k条数据从原来的10s以上降低到300ms左右,还是有着非常大的提升的。整体的1k的分页任务完成,从原来的40s左右降低到1.5s左右。那么完成200w左右的数据,仍然需要接近1个小时,不能满足业务期望

    异步解决方案

    IO的开销,已经基本上没办法在低成本的角度去优化了。那么可以从cpu的角度进行提高,运行top命令后发现,cpu的java占比基本在%0.3以内。因此可以尝试采用多线程的异步方案进行并发处理。起线程的代码如下,起大约10个线程左右,起的太多,会造成数据库连接数超出,导致数据库连接异常

    query.setCurrentPage(1);
    query.setPageSize(100000);// 开10个线程左右,能覆盖200W的数据
    Integer totalInteger = rUserAlipayDAO.countByQuery(query);
    query.setTotalItem(totalInteger);
    do {
    	try {
    		GetDataThread thread = new GetDataThread(query.getStartRow(), query.getEndRow());
    		Thread t = new Thread(thread);
    		t.start();
    	} catch (Throwable t) {
    		logger.error("update  error", t);
    	}
    } while (query.nextPage());
    

    GetDataThread 代码如下,接受10W条左右的数据,进行任务的操作,构造函数如下,主要用于区分每个线程处理的起始位置

    public GetDataThread (Integer startRow, Integer endRow){
    	this.startRow=startRow;
    	this.endRow=endRow;
    }
    

    下面是处理的run方法

    @Override
    public void run() {
    	...
    	query.setStartRow(startRow);
    	query.setPageSize(1000);//每页1K条数据
    	logger.warn("do startRow"+startRow +" thread start...");
    	Long startLong = System.currentTimeMillis();
    	do{
    		//TODO 进行相关的任务处理
    		startRow= startRow+1000;
    		query.setStartRow(startRow);
    	}while(startRow<endRow);
    	
    	logger.warn("Thread startRow"+startRow +"  cost "+(System.currentTimeMillis()-startLong));
    }
    

    调整后,每个线程处理10W条数据,大概2分钟左右完成,但是由于是10个线程同时开工,总体任务的执行时间基本控制在3min以内,完全满足业务期望

    总结

    这个场景非常简单,写这篇文章的目的主要是找到一个案例,让初学者了解如何去分析一段代码存在的性能问题,并且如何针对这些问题进行代码改进。

  • 相关阅读:
    window计时器函数
    日期函数
    字符的方法
    五大主流浏览器与四大内核
    向一个排好序的数组中插入一个数组。
    冒泡与排序
    数组去重方法1,2,3
    有执行语句:console.log(fn2(2)[3]),补充函数,使执行结果为"hello"
    面试题:一个函数的调用 fn(2)(3)(4) 得到的结果位24; 这个函数是柯里化函数运用了闭包原理
    python中append()与extend()方法的区别
  • 原文地址:https://www.cnblogs.com/qiushizhu/p/4154991.html
Copyright © 2011-2022 走看看