解决DataImportHandler从数据库导入大量数据而内存溢出的问题
?
?
?????同时这样做的话也要设置Connection的两个属性:ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY.但跟踪源码发现Solr中已经有这样的设置.于是可以直接在db-data-config.xml中配置即可完成,具体配置如下:
<dataSource name="dsSqlServer" type="JdbcDataSource" driver="com.microsoft.sqlserver.jdbc.SQLServerDriver" batchSize="3000" url="jdbc:sqlserver://192.168.1.5:1433; DatabaseName=testDatabase;responseBuffering=adaptive;selectMethod=cursor" user="sa" password="12345" />
????其中只要在URL中加上responseBuffering=adaptive;selectMethod=cursor即可,无论多大的表根据这个配置Solr都可以从中读取数据并索引完成,当然,前提是不发生什么故障,如网络故障.这一想法也得到了Solr开发人员的验证,以下是他们给我回复的邮件:
??????That's not really true. DataImportHandler streams the result from database query and adding documents into index. So it shouldn't load all database data into memory. Disabling autoCommit, warming queries and spellcheckers usually decreases required amount of memory during
??indexing process.Please share your hardware details, jvm options, solrconfig and schema configuration, etc.
?
??
???????但如此还是会造成一种隐患,就是如果表数据太大.如此会导致Solr一直在那获取数据并且索引.一旦发生什么故障如网络问题,Solr之前所做的索引就会前功尽弃,因为我们根本不知道它索引了多少条,索引到了哪里(后来看代码其实这种担忧是多余的,一旦发生网络异常,Solr会抛出异常,然后进行回滚,将索引恢复到索引之前的状态,也就是说之前做的一切根本
是白做,而Lucene写索引的速度又是相当慢,在如此长的时间内发生不可预见的问题绝对有可能).同时Solr要写完一次索引才会通知IndexSearcher让IndexReader去reopen,所以在那么长一段时间内所索引的数据你是无法搜索到.于是自己想通过ID(Solr里设置的<uniqueKey>)分批来处理.但这个ID是数字要更方便,比如设置个起点,和一次索引多少条.如起点为1,一次索引10000条。如此Solr会从1-10000,10001-20000,如此一直不停的索引下去.本想自己继承个DataImportHandler去处理,后来Solr开发者给我回复的邮件告诉我,Solr通过配置也完全可以实现,邮件内容具体如下:??
??????You can _batch_ import your data using full import command by providing additional request parameter (see
??http://wiki.apache.org/solr/DataImportHandler#Accessing_request_parameters), i.e.
??query="SELECT * FROM my_table ORDER BY id LIMIT 1000000 OFFSET ${dataimporter.request.offset}"
??and then calling full-import command several times:
??1) /dataimport?clean=true&offset=0
??2) /dataimport?clean=false&offset=1000000
??3) /dataimport?clean=false&offset=2000000
??etc
?
????于是我在db-data-config.xml中用如下配置:
<entity name="TestEntity" dataSource="dsSqlServer" pk="Id" query="SELECT Id,Title,Author,Content,Url,AddOn FROM Test WHERE Id>=${dataimporter.request.offset} And Id<=${dataimporter.request.offset}+10000" >
???同时运用?
???1) /dataimport?clean=true&offset=0
??2) /dataimport?clean=false&offset=1000000
??3) /dataimport?clean=false&offset=2000000
??这些URL去请求SOlr进行索引.Solr会读取offset这个参数区建立query查询语句,然后去获取这些查询语句的数据去索引.但如此最好还是自己写个类做下控制.可以一开始获取表的总结果条数然后通过以上循环迭代完成.其中里面的+10000也可以设置为一个参数,如${dataimporter.request.intervalRows}表示一次获取多少条,然后在URL里加&intervalRows这个参数,具体我还还没试过.同时由于索引还是个长期的过程,所以这里最好记录好每次索引索引成功的间隔.比如在30000-40000索引成功的时候将他们记录日志.如此在索引40000-50000的时候一旦发生问题,我们可以通过日志获取上一次成功索引的间隔数,再从这个间隔去重新生成URL去索引数据.
?
?????以上希望起个抛砖引玉的作用,能对大家有所帮助,如果还有什么问题请留言。同时我这里处理的仅仅是SqlServer2005,其他数据库上操作成功的请分享下。同时如果你可以保证你间隔获取的数据如10000-20000这些数据不会导致内存溢出,那么你也可以不使用responseBuffering=adaptive;selectMethod=cursor这种配置.直接通过设置请求URL的offset来处理.
?