solr-dataimportHandler之批量索引

            本文主要讨论solr中的dataImportHandler机制,对这个不熟的朋友,可以先看下。solr  wiki中的dataimporthandler这篇文章,笔者也对dataimporthandler进行了一些翻译,不过效果不是很好,有兴趣的朋友也可以参考一下。  http://mxsfengg.blog.163.com/blog/static/26370218200810250524813/

           想对比较多的数据建立索引,当然要考虑一个量的问题。之前怀疑sqlEntityProcessor是一条条的去数据库中取数据的,因为还有个CachedSqlEntityProcessor,这个类的名字看起来更像是一个能够批量去数据的家伙。

          当然事实证明,笔者之前的想法是错的。

         对于完全导入、即是full-import,来说,我们只需要关注nextRow() 方法的实现。

         

public Map<String, Object> nextRow() {

		//从缓存中取(在sqlEntityProcessor中这个应该是null,因为在设计上,sqlEntityPorcessor并没有实现缓存。)
		if (rowcache != null) {
			return getFromRowCache();
		}
		// 这个只是为了防止rowIterator==null的出现吧
		if (rowIterator == null) {
			String q = getQuery();
			initQuery(resolver.replaceTokens(q));
		}
		while (true) {

			Map<String, Object> r = getNext();
			if (r == null) {
				return null;
			}
			// 使用转化器
			r = applyTransformer(r);
			if (r != null) {
				return r;
			}
		}

	}

            我们可以看到,nextRow一开始去检测 rowcache,这应该是一个缓存的机制,目前,还没有遇到rowcache!=null的情况。我们发现nextRow实际上调用的是getNext(),

 protected Map<String, Object> getNext() {
    try {
      if (rowIterator == null) {
		return null;
	}
      if (rowIterator.hasNext()) {
		return rowIterator.next();
	}
      //如果一个批次结束了,那么就将这个rowIterator,query置为null。
      rowIterator = null;
      query = null;
      return null;
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "getNext() failed for query '" + query + "'", e);
      rowIterator = null;
      query = null;
      throw new DataImportHandlerException(DataImportHandlerException.WARN, e);
    }
  }

         而getNext(),又是简单的调用了,rowIterator的next,我们可以这么认为,nextRow()其实就是返回rowIterator的next()。

         SqlEntityProcessor一次性的从数据库中将数据取出来放在了rowIterator中,供DocBuilder调用。

         粗略的看一下 Docbuilder中的buildDocument这个方法。

 

@SuppressWarnings("unchecked")
  private void buildDocument(VariableResolverImpl vr, SolrWriter.SolrDoc doc,
                             Map<String, Object> pk, DataConfig.Entity entity, boolean isRoot,
                             ContextImpl parentCtx) {

  .............................................
    try {
      while (true) {
           ...................................................................
          Map<String, Object> arow = entityProcessor.nextRow();
          if (arow == null)
            break;

               }

        } catch (DataImportHandlerException e) {
          if (verboseDebug) {
            writer.log(SolrWriter.ENTITY_EXCEPTION, entity.name, e);
          }
          if (isRoot) {
            if (e.getErrCode() == DataImportHandlerException.SKIP) {
              importStatistics.skipDocCount.getAndIncrement();
            } else {
              LOG.log(Level.SEVERE, "Exception while processing: "
                      + entity.name + " document : " + doc, e);
            }
            if (e.getErrCode() == DataImportHandlerException.SEVERE)
              throw e;
          } else
            throw e;
        } finally {
          if (verboseDebug) {
            writer.log(SolrWriter.ROW_END, entity.name, null);
            if (entity.isDocRoot)
              writer.log(SolrWriter.END_DOC, null, null);
          }
        }
      }
    } finally {
      if (verboseDebug) {
        writer.log(SolrWriter.END_ENTITY, null, null);
      }
    }
  }

      这个方法使用while循环一直调用nextrow()方法,直到nextRow返回null值,仔细观察getNext(),我们会发现当rowIterator取完的时候,就会返回null了,而这时候,也就跳出了while这个循环。

      那就是说,sqlEntity会将执行query语句,并将所有的结果一次取回放到rowIterator中。

      现在的问题在于,如果数据库的数量够大,一次取完所有的数据就变得不现实了,那么要怎样才能够实现批次取数据?

      以下是笔者实现的一个例子,当然这个例子有很多纰漏,需要去改进,放在这里当作抛砖引玉吧。

      这是要索引的表:

     

people  CREATE TABLE `people` (                  
          `id` int(11) NOT NULL auto_increment,  
          `name` varchar(20) NOT NULL,           
          PRIMARY KEY  (`id`)                    
        ) ENGINE=InnoDB DEFAULT CHARSET=latin1 

 

      一般情况下,相应的dataConfig.xml文件,我们可以这么写:

  

       <entity name="y" query="select * from people ">
                   <field column="id" name="id" />
                   <field column="name" name="name" />

      </entity>

        这个结构就会一次性从数据库中取完所有的数据,放在rowIterator中,但现在我们并不想这样去实现,以适应更多的数据量。笔者增加了一个表。下面是这个表的数据。

+-----+---------+
| id  | item    |
+-----+---------+
|   1 |       0 |
|   5 |   10000 |
|   6 |   50000 |
|   7 |   60000 |
|   8 |   70000 |
|   9 |   80000 |
|  10 |   90000 |
|  11 |  100000 |
|  12 |  110000 |
|  13 |  120000 |
|  14 |  130000 |
|  15 |  140000 |
|  16 |  150000 |

       修改dataconfig.xml文件

   

<entity name="x" query="select * from item" rootEntity="false">
        <entity name="y" query="select * from people where id between ${x.item} and ${x.item}+1000">
	   <field column="id" name="id" />
                   <field column="name" name="name" />

       </entity>
</entity>

 

         显而易见,笔者是通过增加一个表的方式来达到控制批量存取的效果的。虽然这不失为问题的解决方法,不过增加一个表来这个步骤实在让人觉得有点繁琐,且通用性不强。

         当然我们也可以使用xml来代替数据库中的表,不过这只是一种换汤不换药的方式罢了。

         或许,通过继承SqlEntityProcessor,覆盖nextRow方法,扩展它的功能,也是一种可以尝试的选择。

     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值