• Solrj和Solr DIH索引效率对比分析


    测试软件环境:

        1、16G windows7 x64  32core cpu 。

        2、jdk 1.7  tomcat 6.x  solr 4.8

    数据库软件环境:

        1、16G windows7 x64  32core cpu 。

        2、Oracle 11g 

    一、Solr默认索引工具DIH。

      使用Solr DIH索引数据,一千九百万数据,耗时45分钟左右,每秒钟6500条/s,合计39w条每分钟。

      相关jvm最大堆内存为4G,solr index config使用默认参数。

      Solr DIH 导入截图:

      

      导入2500w条数据总耗时一个小时左右

      

      索引字段,总共15个左右

      

      (备注:字段越少,字段值越小,索引的速度也越快,因此优化Solr查询和索引效率,schema设计显得尤为重要)

    二、Solrj API 索引数据。

      使用Solrj api效率稍差,合计30w每秒,耗时一个多小时。

      Solr Server配置参数同上。在客户端机器上,读取数据库数据,使用Solrj api进行索引。代码如下:

      

    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.UUID;
    import org.apache.solr.client.solrj.SolrServer;
    import org.apache.solr.client.solrj.SolrServerException;
    import org.apache.solr.client.solrj.impl.HttpSolrServer;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.util.StringUtils;
    import com.tianditu.search.v2.POI;
    
    public class ImportPOI implements IJobDef{
    
    	private SolrServer server;
    	private DatasourceConfig jdbcConfig;
    	private SolrConfig solrConfig;
    	private POIImportConfig poiConfig;
    	
    	public DatasourceConfig getJdbcConfig() {
    		return jdbcConfig;
    	}
    	public void setJdbcConfig(DatasourceConfig jdbcConfig) {
    		this.jdbcConfig = jdbcConfig;
    	}
    	public SolrConfig getSolrConfig() {
    		return solrConfig;
    	}
    	public void setSolrConfig(SolrConfig solrConfig) {
    		this.solrConfig = solrConfig;
    	}
    	public POIImportConfig getPoiConfig() {
    		return poiConfig;
    	}
    	public void setPoiConfig(POIImportConfig poiConfig) {
    		this.poiConfig = poiConfig;
    	}
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		ApplicationContext context = new ClassPathXmlApplicationContext("app-spring.xml");
    		ImportPOI importTool = (ImportPOI) context.getBean("importPOITool");
    		importTool.submit(new JobDoneCallBack() {
    			
    			public void onCallback(JobStatus status) {
    				// TODO Auto-generated method stub
    				System.out.println(status.getStatus());
    				System.out.println(status.getMessage());
    			}
    		},new JobTimer() {
    			
    			public void onTimeUpdate(long timeCost) {
    				// TODO Auto-generated method stub
    				System.out.println("solr提交一次,距任务开始已耗时:"+timeCost/(1000*60)+"分钟");
    				
    			}
    		});
    
    	}
    	public SolrServer getServer() {
    		return server;
    	}
    	public void setServer(SolrServer server) {
    		this.server = server;
    	}
    	
    	public boolean importPOI(HashMap<String, Object> params){
    		return false;
    		
    	}
    	
    	
    	private POI  getPOI(ResultSet rs) throws SQLException{
    		POI poi = new POI();
    		
    		poi.setId((UUID.randomUUID()).toString());
    		poi.setName(rs.getString("nameforStore"));
    		poi.setAddress(rs.getString("addressforStore"));
    		
    		String lat = rs.getString("lat");
    		
    		if(lat!=null&&!lat.equalsIgnoreCase("null")&&lat.length()>0){
    			poi.setLat(Double.valueOf(lat));
    		}
    		
    		String lon = rs.getString("lon");
    		
    		//poi.setLon(rs.getDouble("lon"));
    		
    		if(lon!=null&&!lon.equalsIgnoreCase("null")&&lon.length()>0){
    			poi.setLon(Double.valueOf(lon));
    		}
    		
    		poi.setNid(rs.getString("DOCID"));
    		
    		String totalCity = rs.getString("totalcity");
    		if(!StringUtils.isEmpty(totalCity)){//---------citycode
    			String[] cities = totalCity.split(" ");
    			List<String> cs = new ArrayList<String>();
    			for(String c:cities){
    				cs.add(c);
    			}
    			poi.setCities(cs);
    		}
    		
    		String types = rs.getString("type");
    		if(!StringUtils.isEmpty(types)){//type-----------------
    			String[] typea = types.split(" ");
    			List<String> t = new ArrayList<String>();
    			for(String c:typea){
    				t.add(c);
    			}
    			//poi.setCities(cs);
    			poi.setTypes(t);
    		}
    		
    		return poi;
    	};
    	public void submit(JobDoneCallBack callback,JobTimer timer) {
    
    		if(solrConfig==null){
    			throw new IllegalArgumentException("SolrJ未正确配置.");
    		}
    		
    		if(jdbcConfig == null){
    			
    			throw new IllegalArgumentException("JDBC未正确配置.");
    		}
    		
    		if(poiConfig == null){
    			throw new IllegalArgumentException("POI配置文件未正确配置.");
    		}
    		
    		Connection con = null;
    		Statement pst = null;
    		ResultSet rs = null;
    		
    		SolrServer  ss = null;
    		
    		JobStatus status = new JobStatus();
    		status.setName("ImportPOI");
    		status.setStatus("failure");
    		
    		int i = 0;
    		int c = 0;
    		long start = System.currentTimeMillis();
    		try {
    				
    				Class.forName(jdbcConfig.getDriverClass()).newInstance();
    				con = DriverManager.getConnection(jdbcConfig.getUrl(), jdbcConfig.getUserName(), jdbcConfig.getPassWord());
    				
    				int batchSize = Integer.valueOf(poiConfig.getImportRecordSize());
    				ss = new HttpSolrServer(solrConfig.getSolrUrl());
    				if(poiConfig.isDeleteOnstartup()){
    					ss.deleteByQuery("*:*");
    					ss.commit();
    				}
    				if(jdbcConfig.getDriverClass().toString().contains("mysql")){//mysql
    					pst =  (com.mysql.jdbc.Statement) con.createStatement(ResultSet.FETCH_FORWARD,ResultSet.CONCUR_READ_ONLY);
    					pst.setFetchSize(1);
    					((com.mysql.jdbc.Statement) pst).enableStreamingResults();
    				}else{
    					pst =  con.createStatement();
    				}
    				
    				rs = pst.executeQuery(poiConfig.getImportSQL());
    				
    				POI p = null;
    				
    				List<POI> pois = new ArrayList<POI>();
    				
    				while(rs.next()){
    					
    					p = getPOI(rs);
    					
    					//ss.addBean(p);
    					pois.add(p);
    					if(i>=batchSize){
    						long commitT = System.currentTimeMillis();
    						//System.out.println("已耗时:"+(commitT-start)/1000*60+"分钟");
    						timer.onTimeUpdate((commitT-start));
    						//System.out.println("提交一次");
    						ss.addBeans(pois);
    						ss.commit();
    						pois.clear();
    						c++;
    						i=0;
    					}else{
    						i++;
    					}
    					
    				}
    				ss.addBeans(pois);
    				ss.commit();
    				long end = System.currentTimeMillis();
    				status.setStatus("success");
    				status.setMessage("处理成功,总耗时:"+(end-start)/1000*60+"分钟");
    				status.setTimeCost((end-start)/1000*60);
    
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			//e.printStackTrace();
    			status.setMessage(e.toString());
    		} catch (ClassNotFoundException e) {
    			// TODO Auto-generated catch block
    			//e.printStackTrace();
    			status.setMessage(e.toString());
    		} catch (InstantiationException e) {
    			// TODO Auto-generated catch block
    			//e.printStackTrace();
    			status.setMessage(e.toString());
    		} catch (IllegalAccessException e) {
    			// TODO Auto-generated catch block
    			//e.printStackTrace();
    			status.setMessage(e.toString());
    		} catch (SolrServerException e) {
    			// TODO Auto-generated catch block
    			//e.printStackTrace();
    			status.setMessage(e.toString());
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			//e.printStackTrace();
    			status.setMessage(e.toString());
    		}finally{
    			
    
    			try {
    				if(rs!=null){
    				rs.close();
    				}
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			
    			}
    			try {
    				if(pst!=null)pst.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			try {
    				if(con!=null)
    				con.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			
    			if(callback!=null){
    				callback.onCallback(status);
    			}
    		}
    		//return false;
    	};
    	
    	
    
    }
    

      整个过程是读取数据库,将数据转成DTO,然后通过SolrServer.addBeans插入solr server,调用SolrServer.commit进行索引提交(就可以查询结果)。

      从数据库中读取转换过程代码如下:

      

    	private POI  getPOI(ResultSet rs) throws SQLException{
    		POI poi = new POI();
    		
    		poi.setId((UUID.randomUUID()).toString());
    		poi.setName(rs.getString("nameforStore"));
    		poi.setAddress(rs.getString("addressforStore"));
    		
    		String lat = rs.getString("lat");
    		
    		if(lat!=null&&!lat.equalsIgnoreCase("null")&&lat.length()>0){
    			poi.setLat(Double.valueOf(lat));
    		}
    		
    		String lon = rs.getString("lon");
    		
    		//poi.setLon(rs.getDouble("lon"));
    		
    		if(lon!=null&&!lon.equalsIgnoreCase("null")&&lon.length()>0){
    			poi.setLon(Double.valueOf(lon));
    		}
    		
    		poi.setNid(rs.getString("DOCID"));
    		
    		String totalCity = rs.getString("totalcity");
    		if(!StringUtils.isEmpty(totalCity)){//---------citycode
    			String[] cities = totalCity.split(" ");
    			List<String> cs = new ArrayList<String>();
    			for(String c:cities){
    				cs.add(c);
    			}
    			poi.setCities(cs);
    		}
    		
    		String types = rs.getString("type");
    		if(!StringUtils.isEmpty(types)){//type-----------------
    			String[] typea = types.split(" ");
    			List<String> t = new ArrayList<String>();
    			for(String c:typea){
    				t.add(c);
    			}
    			//poi.setCities(cs);
    			poi.setTypes(t);
    		}
    		
    		return poi;
    	};
    

      SolrJ索引过程代码:

      

    				List<POI> pois = new ArrayList<POI>();
    				
    				while(rs.next()){//遍历JDBC ResultSet
    					
    					p = getPOI(rs);
    					
    					//ss.addBean(p);
    					pois.add(p);
    					if(i>=batchSize){//定量批量索引逻辑
    						long commitT = System.currentTimeMillis();
    						//System.out.println("已耗时:"+(commitT-start)/1000*60+"分钟");
    						timer.onTimeUpdate((commitT-start));
    						//System.out.println("提交一次");
    						ss.addBeans(pois);//发向SolrServer
    						ss.commit();
    						pois.clear();
    						c++;
    						i=0;
    					}else{
    						i++;
    					}
    					
    				}
    				ss.addBeans(pois);//做最后提交
    				ss.commit();
    

      分析:

        1、性能差别主要在哪里?

        答:方案一和方案主要差别在于,方案一访问数据之后直接调用Solr内部UpdateHandler,直接将数据放入索引。而方案二,调用SolrJ索引数据,多了一道网络IO。而且,方案二,在solrj索引之前,先将数据转换为DTO,然后Solrj将DTO转换为SolrInputDocument对象,然后SolrInputDocument对象转换成solr rest 接口所需字符串,中间有多处转换,也存在性能损耗(备注:调用Solrj addBeans批量导入索引的方法是提高性能的方式,如果一个一个的提交,性能会更差,http请求更多)。

        2、怎么优化?

        答:问题一的分析,就是问题二的答案。主要那么多数据实体转换那块,主要遵守:1、使用调用接口尽量简单,使用ResultSet直接转换成SolrInputDocument对象,少一些数据转换。2、使用数组等数据结构,替换掉目前的List<Bean>。

        3、使用Solr EmbededSolrServer直接创建索引是否能提高效率?

        答:经过测试EmbededSolrServer 可以提高索引效率,大约是DIH的一倍多。使用方式如下代码所示: 

        private SolrServer getSolrServer(){
            // System.setProperty("solr.solr.home", "R:\solrhome1\solr\POI\"); 
             CoreContainer coreContainer = new CoreContainer("R:\solrhome1\solr\");
             coreContainer.load();//初始化
    //         while(!coreContainer.isLoaded("POI")){
    //             System.out.println("loading...");
    //         }
             System.out.println(coreContainer.getAllCoreNames());
             server = new EmbeddedSolrServer(coreContainer,"POI");
            return server;
            
        }

      (备注:EmbededSolrServer保证程序运行在Solr服务器上,是无法通过http方法的,使用场景通常是两个core,一个用此方法,完成以后,swarp一下这个core,让其对外提供检索服务)

        文章转载,请注明出处:http://www.cnblogs.com/likehua/p/4465514.html

  • 相关阅读:
    云架构师进阶攻略(3)
    微服务化之服务拆分与服务发现
    终于有人把云计算、大数据和人工智能讲明白了!(1)
    JavaScript的数组详解
    html中给元素添加背景图片或者gif动图
    JavaScript的事件
    JavaScript的匿名函数
    JavaScript获取和操作html的元素
    JavaScript的条件运算符与条件语句
    JavaScript变量、数据类型、函数
  • 原文地址:https://www.cnblogs.com/likehua/p/4465514.html
Copyright © 2020-2023  润新知