zoukankan      html  css  js  c++  java
  • solrdataimportHandler之批量索引

    http://mxsfengg.iteye.com/blog/277913  

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

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

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

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

             

    Java代码  收藏代码
    1. public Map<String, Object> nextRow() {  
    2.   
    3.         //从缓存中取(在sqlEntityProcessor中这个应该是null,因为在设计上,sqlEntityPorcessor并没有实现缓存。)  
    4.         if (rowcache != null) {  
    5.             return getFromRowCache();  
    6.         }  
    7.         // 这个只是为了防止rowIterator==null的出现吧  
    8.         if (rowIterator == null) {  
    9.             String q = getQuery();  
    10.             initQuery(resolver.replaceTokens(q));  
    11.         }  
    12.         while (true) {  
    13.   
    14.             Map<String, Object> r = getNext();  
    15.             if (r == null) {  
    16.                 return null;  
    17.             }  
    18.             // 使用转化器  
    19.             r = applyTransformer(r);  
    20.             if (r != null) {  
    21.                 return r;  
    22.             }  
    23.         }  
    24.   
    25.     }  

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

    Java代码  收藏代码
    1. protected Map<String, Object> getNext() {  
    2.    try {  
    3.      if (rowIterator == null) {  
    4.     return null;  
    5. }  
    6.      if (rowIterator.hasNext()) {  
    7.     return rowIterator.next();  
    8. }  
    9.      //如果一个批次结束了,那么就将这个rowIterator,query置为null。  
    10.      rowIterator = null;  
    11.      query = null;  
    12.      return null;  
    13.    } catch (Exception e) {  
    14.      LOG.log(Level.SEVERE, "getNext() failed for query '" + query + "'", e);  
    15.      rowIterator = null;  
    16.      query = null;  
    17.      throw new DataImportHandlerException(DataImportHandlerException.WARN, e);  
    18.    }  
    19.  }  

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

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

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

     

    Java代码  收藏代码
    1. @SuppressWarnings("unchecked")  
    2.   private void buildDocument(VariableResolverImpl vr, SolrWriter.SolrDoc doc,  
    3.                              Map<String, Object> pk, DataConfig.Entity entity, boolean isRoot,  
    4.                              ContextImpl parentCtx) {  
    5.   
    6.   .............................................  
    7.     try {  
    8.       while (true) {  
    9.            ...................................................................  
    10.         <span style="color: #ff0000;">  Map<String, Object> arow = entityProcessor.nextRow();  
    11.           if (arow == null)  
    12.             break;  
    13.   
    14.                }</span>  
    15.   
    16.         } catch (DataImportHandlerException e) {  
    17.           if (verboseDebug) {  
    18.             writer.log(SolrWriter.ENTITY_EXCEPTION, entity.name, e);  
    19.           }  
    20.           if (isRoot) {  
    21.             if (e.getErrCode() == DataImportHandlerException.SKIP) {  
    22.               importStatistics.skipDocCount.getAndIncrement();  
    23.             } else {  
    24.               LOG.log(Level.SEVERE, "Exception while processing: "  
    25.                       + entity.name + " document : " + doc, e);  
    26.             }  
    27.             if (e.getErrCode() == DataImportHandlerException.SEVERE)  
    28.               throw e;  
    29.           } else  
    30.             throw e;  
    31.         } finally {  
    32.           if (verboseDebug) {  
    33.             writer.log(SolrWriter.ROW_END, entity.name, null);  
    34.             if (entity.isDocRoot)  
    35.               writer.log(SolrWriter.END_DOC, nullnull);  
    36.           }  
    37.         }  
    38.       }  
    39.     } finally {  
    40.       if (verboseDebug) {  
    41.         writer.log(SolrWriter.END_ENTITY, nullnull);  
    42.       }  
    43.     }  
    44.   }  

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

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

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

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

          这是要索引的表:

         

    Sql代码  收藏代码
    1. people  CREATE TABLE `people` (                    
    2.           `id` int(11) NOT NULL auto_increment,    
    3.           `namevarchar(20) NOT NULL,             
    4.           PRIMARY KEY  (`id`)                      
    5.         ) ENGINE=InnoDB DEFAULT CHARSET=latin1   

     

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

      

    Xml代码  收藏代码
    1. <entity name="y" query="select * from people ">  
    2.             <field column="id" name="id" />  
    3.             <field column="name" name="name" />  
    4.   
    5. lt;/entity>  

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

    Sql代码  收藏代码
    1. +-----+---------+  
    2. | id  | item    |  
    3. +-----+---------+  
    4. |   1 |       0 |  
    5. |   5 |   10000 |  
    6. |   6 |   50000 |  
    7. |   7 |   60000 |  
    8. |   8 |   70000 |  
    9. |   9 |   80000 |  
    10. |  10 |   90000 |  
    11. |  11 |  100000 |  
    12. |  12 |  110000 |  
    13. |  13 |  120000 |  
    14. |  14 |  130000 |  
    15. |  15 |  140000 |  
    16. |  16 |  150000 |  

           修改dataconfig.xml文件

       

    Xml代码  收藏代码
    1. <entity name="x" query="select * from item" rootEntity="false">  
    2.         <entity name="y" query="select * from people where id between ${x.item} and ${x.item}+1000">  
    3.        <field column="id" name="id" />  
    4.                    <field column="name" name="name" />  
    5.   
    6.        </entity>  
    7. </entity>  

     

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

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

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

  • 相关阅读:
    订单号设计
    小公司的技术架构原则
    Redis配置详解
    实现图片懒加载
    Js的GC机制
    防抖与节流
    Js中的堆栈
    浏览器窗口间通信
    块级格式化上下文
    实现瀑布流布局
  • 原文地址:https://www.cnblogs.com/chenying99/p/2621127.html
Copyright © 2011-2022 走看看