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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM