设为首页 收藏本站
查看: 968|回复: 0

[经验分享] Solrj和Solr DIH索引效率对比分析

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-7-17 08:27:01 | 显示全部楼层 |阅读模式
  测试软件环境:
  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 导入截图:
DSC0000.png
  导入2500w条数据总耗时一个小时左右
DSC0001.png
  索引字段,总共15个左右
DSC0002.png
  (备注:字段越少,字段值越小,索引的速度也越快,因此优化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 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 cs = new ArrayList();
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 t = new ArrayList();
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 pois = new ArrayList();
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 cs = new ArrayList();
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 t = new ArrayList();
for(String c:typea){
t.add(c);
}
//poi.setCities(cs);
poi.setTypes(t);
}
return poi;
};

  SolrJ索引过程代码:
  



List pois = new ArrayList();
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。
      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.iyunv.com/likehua/p/4465514.html
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-87441-1-1.html 上篇帖子: solr部署(windows) 下篇帖子: Apache Solr facet 分组查询
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表