zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    共享纸巾更换主板代码分析 共享纸巾主板更换后的对接代码
    Python Django Ajax 传递列表数据
    Python Django migrate 报错解决办法
    Python 创建字典的多种方式
    Python 两个list合并成一个字典
    Python 正则 re.sub替换
    python Django Ajax基础
    Python Django 获取表单数据的三种方式
    python Django html 模板循环条件
    Python Django ORM 字段类型、参数、外键操作
  • 原文地址:https://www.cnblogs.com/likehua/p/4465514.html
Copyright © 2011-2022 走看看