Solrj API索引效率对比分析

测试软件环境:

    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 导入截图:

  

二、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>。

时间: 2024-11-02 23:50:14

Solrj API索引效率对比分析的相关文章

多种临时表效率的对比分析

多种临时表效率的对比分析 老帅(20141107) 平时在写存储过程的时候,经常会用到临时表,而临时表的用法到底有几种,哪种效率更好呢?我们来做个对比分析. 实验数据:表a有400万条记录,只查询一个字段Title 1.  直接查询 --开启时间分析 SET STATISTICS TIME ON GO --查询 SELECT Title FROM a --结果 SQL Server 执行时间:占用时间 = 22013 毫秒. 2.变量表 --开启时间分析 SET STATISTICS TIME

solr 的客户端调用solrj 建索引+分页查询

一.利用SolrJ操作solr API 使用SolrJ操作Solr会比利用httpClient来操作Solr要简单.SolrJ是封装了httpClient方法,来操作solr的API的.SolrJ底层还是通过使用httpClient中的方法来完成Solr的操作. 需要的包如下: 1. apache-solr-solrj-3.5.0.jar 2. commons-httpclient-3.1.jar 3.slf4j-api-1.6.0.jar 4.commons-logging-1.1.jar 在

近3年微软与谷歌的发展对比分析

     近3年微软与谷歌的发展对比分析   随着科技的快速发展,时代的不断进步,微软和谷歌凭借这不断的创新已然成为当今全球科技公司的领头羊.位列世界500强的微软是一个相当具有经济与科技实力的的公司,然而同样位列世界500强的谷歌凭借着家喻户晓的Google搜索成为了微软一个相当具有竞争力的科技大亨. 同为IT公司,微软和谷歌的比较如下: 一.发展历史 微软作为一个1975年成立的老牌公司,从一开始的为IBM提供文件系统和操作系统等软件,到现在业务中有各种操作系统编译器和解释器.网页浏览器等基

三大WEB服务器对比分析(apache ,lighttpd,nginx)

一.软件介绍(apache  lighttpd  nginx) 1. lighttpd Lighttpd是一个具有非常低的内存开销,cpu占用率低,效能好,以及丰富的模块等特点.lighttpd是众多OpenSource轻量级的web server中较为优秀的一个.支持FastCGI, CGI, Auth, 输出压缩(output compress), URL重写, Alias等重要功能. Lighttpd使用fastcgi方式运行php,它会使用很少的PHP进程响应很大的并发量. Fastcg

[]H5、React Native、Native应用对比分析

目录(?)[-] 一React Native的出现 二3款应用效果 三工程方案 四对比分析 开发方式 性能 体验 更新 维护 开发方式 性能 体验 更新 维护 五综合 开发方式 性能 体验 更新 维护 @王利华,vczero “存 在即合理”.凡是存在的,都是合乎规律的.任何新事物的产生总要的它的道理:任何新事物的发展总是有着取代旧事物的能力.React Native来的正是时候,一则是因为H5发展到一定程度的受限:二则是移动市场的迅速崛起强调团队快速响应和迭代:三则是用户的体验被放大,用户要求

JAVAEE——Solr:安装及配置、后台管理索引库、 使用SolrJ管理索引库、仿京东的电商搜索案例实现

1 学习回顾 1. Lucene  是Apache开源的全文检索的工具包 创建索引 查询索引 2. 遇到问题? 文件名 及文件内容  顺序扫描法  全文检索 3. 什么是全文检索? 这种先创建索引 再对索引进行搜索的过程叫全文检索 4. 索引是什么? 非结构数据中提取一个数据.并重新组合的过程叫索引 5. Lucene实现 6. 入门程序 磁盘文件为原始文件 创建索引 第一步:获取文件 第二步:创建文档对象 第三步:创建分析器 第四步:保存索引及文档到索引库 搜索索引 第一步:用户接口(百度)

测试工程师的福利!各远程移动测试平台对比分析

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯移动品质中心TMQ发表于云+社区专栏 背景 随着移动设备和系统的碎片化程度越来越高以及复杂的移动网络情况, 兼容性测试以及远程真机测试的重要性越来越突出.根据远程测试机/人员与开发者间的合作方式,可以分为以下几种服务:云测试服务.内测服务以及众测服务,相应的平台支持如下图. 云测试平台 云测试平台提供了远程租用真机的服务,通常是利用自动化框架来实现真机上的脚本自动化运行,或远程租用真机人工测试,或真人真机测试.由于Androi

重新学习MySQL数据库5:根据MySQL索引原理进行分析与优化

重新学习MySQL数据库5:根据MySQL索引原理进行分析与优化 一:Mysql原理与慢查询 MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓"好马配好鞍",如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如"精通MySQL"."SQL语句优化"."了解数据库原理"等要求.我们知道一般的应用系统,读写比例在10:1左右,而且插入

GitHub &amp; Bitbucket &amp; GitLab &amp; Coding 的对比分析

来源于:https://www.v2ex.com/t/313263 目前在代码托管和版本控制上的主流工具 — Git ,比较流行的服务有 Github . Bitbucket . GitLab . Coding ,他们各自有什么特点,个人使用者和开发团队又该如何选择? 在这篇文章中,我们以客观的态度,以问题作为出发点,介绍和比较 GitHub . Bitbucket . GitLab . Coding 在基本功能,开源与协作,免费与付费计划,企业解决方案,集成 flow.ci 等方面,让大家了解