当前位置: 代码迷 >> Java相关 >> Solrj跟Solr DIH索引效率对比分析
  详细解决方案

Solrj跟Solr DIH索引效率对比分析

热度:79   发布时间:2016-04-22 20:17:59.0
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

 

  相关解决方案