solr4.8 DataImportHandler 从关系型数据库导入数据
1. 概述
大多数的应用程序将数据存储在关系数据库(例如oracle、mysql、sql service等等)。对这样的数据进行搜索是很常见的应用。对于这样的应用如果技术选型是Solr,就需要把数据从关系型数据库导入到solr服务器,为了解决这个问题,Apache提供了DataImportHandler,所谓的DataImportHandler就是提供一种可配置的方式向solr导入数据,可以一次全部导入,也可以增量导入,还可以声明式提供可配置的任务调度,让数据定时的从关系型数据库更新数据到solr服务器。
2. 使用步骤
- 将solr发布包下面的dist/solr-dataimporthandler-extras-4.8.0.jar 和 solr-dataimporthandler-4.8.0.jar
导入到solr的lib目录下面
在需要进行数据导入的core里面引入DataImportHandler 代码如下
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">/home/item /data-config.xml</str> </lst> </requestHandler>
从它的名字上,我们或许也可以猜到,DataImportHandler正是requestHandler的实现。- 新建data-config.xml
在相应的目录创建上面的data-config.xml文件,不过这个文件一般都是放在conf目录下面,那么这时的应该这么写:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">data-config.xml</str> </lst> </requestHandler>
4.配置data-config.xml
最简单的实例如下:
<dataConfig> <!-- 数据源 --> <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/dih" user="ickes" password="ickes"/> <document> <entity name="item" query="select id,name from item"> <!-- column:数据库的字段名;name:solr的字段名 --> <field column="id" name="id" /> <field column="name" name="name" /> <field column="type" name="type" /> </entity> </document> </dataConfig>
最后记得把相应数据库的驱动包导入,导入后重启tomcat,进入你的solr管理页面,如下图:
当然你可以可以再url里面直接这样请求:
http://192.168.238.133:8080/solr/dih_test/dataimport?command=full-import
所带参数含义:
commad:该参数可以取值:full-import、delta-import、reload-config、abort
full-import:代表全量导入;
delta-import:增量导入
reload-config : 如果data-config.xml已经改变,你不希望重启solr,而要重新加载配置时,就用这个值;
abort : 如果参数是该值,那么会终止一个在运行的操作;
其他参数:
clean : (default 'true'). 决定在建立索引之前,删除以前的索引;
commit : (default 'true'). 决定这个操作之后是否要commit;
optimize : (default 'true'). 决定这个操作之后是否要优化;
让一个实例跑起来是不是如此的简单,下面会循序渐进的深入……!
3.DataImportHandler的参数说明
在使用步骤中第一步:引入DataImportHandler,第二步:建立data-config.xml文件,这两个步骤是固定的,必须配置,说白了DataImportHandler需要配置就只有data-config.xml文件。
a) 配置数据源
如果只有一个数据源,那么可以像上面第一个实例一样配置,那么所有的entity节点中配置的sql都会引用该数据源。
如果有多个数据源,可以如下这么配置,在dataSource标签上面定义一个name的属性,注意这个name的名字必须唯一,然后在entity标签上面通过dataSource属性引用配置的dataSource。
<dataSource type="JdbcDataSource" name="ds-1" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://db1-host/dbname" user="xxx" password="xxx"/> <dataSource type="JdbcDataSource" name="ds-2" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://db1-host/dbname" user="xxx" password="xxx"/> <entity name="one" dataSource="ds-1" ...> </entity> <entity name="two" dataSource="ds-2" ...> </entity>
多数据源配置就是如此的简单,你没有看错…..!
b) 配置entity
Entity的配置相对来说没有上面的那么” 如此的简单”,当然也是相当容易的,毕竟是Apache的东西,对吧!
DataImportHander为了能够从数据获得想要的数据,设计支持标准的sql规范.以下是entiry的属性含义,来源于官网的翻译,英语不是很好,这是根据我理解翻译的。如果你英语厉害,可以自己去官网上浏览学习,地址如下:http://wiki.apache.org/solr/DataImportHandler
- name(必需的):name是唯一的,用以标识entity
processor:只有当datasource不是RDBMS时才是必需的。默认值是SqlEntityProcessor
transformer:转换器将会被应用到这个entity上,下面章节会继续详细介绍。
pk:entity的主键,它是可选的,但使用“增量导入”的时候是必需。它跟schema.xml中定义的uniqueKey没有必然的联系,但它们可以相同。
rootEntity:默认情况下,document元素下就是根实体了,如果没有根实体的话,直接在实体下面的实体将会被看做根实体。对于根实体对应的数据库中返回的数据的每一行,solr都将生成一个document。
query:是获取全部数据的SQL;用于全量导入
- deltaQuery:查询出所有经过修改的记录的ID ,可能是修改操作,添加操作,删除操作产生的;(此查询只对增量导入起作用,而且只能返回ID值,这个Id对应的是Entity标签的pk属性)
deletedPkQuery: 此操作值查询那些数据库里伪删除的数据的ID(即isdelete标识为1的数据)
solr通过它来删除索引里面对应的数据
(此查询只对增量导入起作用,而且只能返回ID值,这个Id对应的是Entity标签的pk属性)deltaImportQuery:获取增量数据时使用的SQL
parentDeltaQuer:从deltaQuery中取得当前表中更新的行,并把这些行提交给父表。因为,当子表中的一行发生改变时,我们需要更新它的父表的solr文档。
从关系型数据库导入数据到solr服务器建立索引,内容就这么多;
4.综合实例
本实例涵盖了solr从关系型数据库导入的全部,理解本实例是进行企业级开发的关键。
数据库模型的实体关系图如下:
简单解释上面的模型图:
Feature(我叫他特征表)表与item(项目表)是一对多的关系,feature是一方,item是多方,feature表通过item_id引用主表item的id
Item表与category(类别表)表是多对多的关系,该关系通过第三张表item_category进行关联,简单理解就是拆分为两个一对多的关系。这种设计相信大家在开发都干过
如下图所示(原谅的截图,让你不能复制,这是因为如果是文字,文档太丑了)是定义的与之对应的core的schema.xml文件,与数据库这个模型进行映射。 细心的读者可能注意到了feature_description(代表feature表的description字段)和categroy_description(代表categroy表的description字段)这两个字段的multiValued属性的值为ture,这是因为这两个字段与item表是多方的关系。
data-config.xml配置如下图所示这是单独的增量配置:
对上图entity标签(映射配置)解释如下:
这里, 根实体是一个名叫“item”的表,它的主键是id。我使用语句select id,name,manu,weight,price,includes from item读取数据,他的每一条记录都是对应solr的一个document。
从上面的数据库实体关系图可以看出他与feature是一对多的关系如下,注意了,我是怎么处理这种一对多的关系的。
如下图所示:
相同地,我们将item和category连表(它们是多对多的关系)。注意了,我是怎样处理这种对对多的关系的。
如下图所示:
上面的${item.id}表达式的含义相信你现在已经猜出他的含义了,不错他的含义就是引用他父entity中query属性在执行完sql从结果集中取出来的值。
文档写到这里,我们继续深入,上面的例子只是全量导入,下面看我怎么引入增量导入,这次我在item表上面加了一个字段create_time(创建时间),同时data-config.xml的配置如下图所示
这时我最顶层的entity标签中加入一个detaQuery的属性,该属性的含义为查询出所有经过修改的记录的ID ,可能是修改操作,添加操作,删除操作产生的(此查询只对增量导入起作用,而且只能返回ID值,这个Id对应的是Entity标签的pk属性) ;然后将这些更新过的数据,从数据库拉到solr服务器的索引中,更新索引,让数据同步。
该属性的sql条件中有一个表达式{dataimporter.last_index_time},是DataImporthandler传过来的变量,它指出“完全导入”或者“部分导入”的最后运行时间。你可以在data-config.xml文件中的sql的任何地方使用这个变量。对于每次的增量导入、和全量导入这个导入时间都会保存在conf/dataimport.properties文件中,以便下次导入时引用。
注意限制条件:deltaQuery :必须返回自己的pk
的
5.实例进阶
现在有一个问题,对于detaQuery属性可以发现数据库的更新操作,但是有些应用场景要求数据的删除不能物理删除,要求他必须伪删除,也就是在表中定义一个字段isDelete,来判断这条记录是否有效。对于这个问题你可能想到的方法是在solr进行查询时,加一个条件isdelete=0查询出有效不就可以了吗,但是这样做有一个不好的地方,那就是让索引冗余了,这些无效的数据保存在数据库中就可以了,不要交给solr;我相信企业在技术选型上选择solr的原因只有一个,那就是利用solr的建立的倒排索引,来提高检索速度,这样的话索引当然是越小越好,因此这种带isdelte=1的条件查询明显不是好的一个选择。
为了解决这个问题,Apache的攻城狮们在entity标签中引入了另外一个属性,deletedPkQuery这个属性的含义为:查询那些数据库里伪删除的数据的ID(即isdelete标识为1的数据)solr通过它来删除索引里面对应的无效数据 (此查询只对增量导入起作用,而且只能返回ID值,这个Id对应的是Entity标签的pk属性)。
为了测试这个过程,我在item表中又加了一个字段isDelete(国际惯例0代表有效、1代表无效)来标识这条记录是不是有效的,相应的我的data-config.xml变成如图下所示:
这时伪删除的场景业务模型得到了解决。
我还是循序渐进,接下来解决的问题是:对于上面的配置,有这么一个问题,那就是增量导入时,上面配置只能捕捉到父entity(也就是item表)数据的变动,从而将数据更新到solr,如果现在的业务是这样的,那就是当关联表(feature表、category表)的数据变动时也能联动更新索引,这个怎么做呢?强大的Apache当然考虑到了,说多了都是扯淡,直接上实例代码,如下图所示:
从上面代码中可以看到,我在子entity标签中加入了parentDeltaQuer,跟deltaQuery属性,其中parentDeltaQuer属性的含义如下:从deltaQuery中取得当前表中更新的行,并把这些行提交给父表。因为,当子表中的一行发生改变时,我们需要更新它的父表的solr索引。这样一来上面的问题便得到了解决。
限制条件
子Entity的parentDeltaQuery必须引用自己的pk
子Entity的parentDeltaQuery必须返回父Entity的pk
看清楚了,一个是引用,一个是返回
6.原理
Full Import工作原理:
先删除当前core里面的所有数据;
执行本Entity的Query,获取所有数据;
针对每个行数据Row,获取pk,组装子Entity的Query;
执行子Entity的Query,获取子Entity的数据;
Delta Import工作原理:
执行Entity的deltaQuery,获取变化数据的pk;
合并所有子Entity parentDeltaQuery得到的主表中变化的pk;
(变化pk:包括数据中新增、修改、删除的pk,这里的变化是指数据库的变化)
得到变化pk以后,先从solr中删除在数据库中修改和删除过的索引,然后添加新增、修改、删除的索引
如果配置了deltaImportQuery,执行deltaImportQuery,将他获得数据全部插入
从这里可以看出deltaImportQuery往往是多余的,因为deltaQuery会将更新的数据插入(就已经包括了新增的数据)
7.数据同步总结
从oracle同步数据时,日期转换处理:
<entity name="users" query="select to_char(birthday,'yyyy-MM-dd HH24:mi:ss') as birthday from USERS" transformer="DateFormatTransformer"> <field column="SIGNAL_TIME" name="SIGNAL_TIME" dateTimeFormat="yyyy-MM-dd HH:mm:ss"/> </entity>
8.任务调度
任务调度有两种,想知道怎么使用这两种,请听下篇分析;