• solr 高级进阶,解决问题


    一、多表、多数据源 数据导入的问题
    之前介绍过通过data-config.xml 导入数据的问题,实际开发过程中可能会遇到多表、甚至是多数据源的问题,以下我根据实际业务场景说下该如何解决该问题。
    只需要,更改data-config.xml的写法就行了。
    <dataConfig>
    <!-- <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_studio" user="yxj" password="yxj@2015" /> -->

    <dataSource name ="db_yxj_content" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_content" user="xxx" password="xxx" />
    <dataSource name ="db_yxj_user" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_user" user="xxx" password="xxx" />
    <document name = "insert_test">
    <entity name="video" transformer="DateFormatTransformer"
    query="SELECT
    CONCAT(v.vdoid,'video') as id,
    v.vdoid AS vdoid,
    v.catid AS catid,
    v.catids AS catids,
    v.proid AS proid,
    v.title AS title,
    ifnull(v.content, '') AS content,
    v.num AS num,
    v.vdo_type AS vdoType,
    v.play_url AS playUrl,
    LEFT (
    FROM_UNIXTIME(v.publish_time),
    10
    ) AS publishTime,
    v. STATUS AS STATUS,
    v.keywords AS keywords,
    v.coverpic_url AS coverpicUrl,
    v.end_time AS endTime,
    v.start_time AS startTime,
    FROM_UNIXTIME(
    v.update_time,
    '%Y-%m-%d %H:%m:%s'
    ) AS updateTime,
    v.isTop AS isTop,
    parent_id AS parentId,
    p.proid AS proid,
    ifnull(p. NAME, '') AS proName,
    ifnull(p.org, '') AS proOrg,
    ifnull(p.title, '') AS proTitle,
    ifnull(p.department, '') AS proDepartment,
    ifnull(CONCAT(p. NAME, p.title, ' ', p.org), '') AS tempContent
    FROM
    db_yxj_content.t_video v
    LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
    WHERE
    v. STATUS = 0
    AND (
    (
    v.vdo_type = 1
    AND (
    classify_flag = 2
    OR length(v.play_url) > 0
    )
    )
    OR v.vdo_type = 10
    )">
    <field column='publishTime' dateTimeFormat='yyyy-MM-dd' />
    <field column='updateTime' dateTimeFormat='yyyy-MM-dd HH:mm:ss' />
    </entity>
    </document>

    </dataConfig>
    dataSource : 数据源的配置,这里用的是mysql ,如果是多数据源需要根据数据库的连接属性,配置多个相对应的dataSource
    sql: document 下的entity 中 sql 的写法,需要把 数据库名称写上,如上写法 数据库名.表名:db_yxj_content.t_video

    二、全量导入、增量导入的问题


    之前介绍过通过页面操作数据导入的方法。接下来介绍一下 ,如何配置 增量以及全量导入。
    1.全量导入
    全量导入很简单,只要成功配置好data-config.xml 就可以通过点击Execute 按钮 完成全量导入。
    2.增量导入
    这里主要介绍下 增量导入的问题。顾名思义,增量导入即只导入部分数据,这在实际开发中一定是用的到的,因为在我们上线前,可以把数据一次性导入solr完成数据同步,但是上线后新产生的数据或者某些数据被删除或者修改,则需要动过增量导入的方式同步到solr中,data-config.xml配置如下:
    <dataConfig>
    <!-- <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_studio" user="yxj" password="yxj@2015" /> -->

    <dataSource name ="db_yxj_content" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_content" user="xxx" password="xxx" />
    <dataSource name ="db_yxj_user" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_user" user="xxx" password="xxx" />
    <document name = "insert_test">
    <entity name="video" transformer="DateFormatTransformer"
    query="SELECT
    CONCAT(v.vdoid,'video') as id,
    v.vdoid AS vdoid,
    v.catid AS catid,
    v.catids AS catids,
    v.proid AS proid,
    v.title AS title,
    ifnull(v.content, '') AS content,
    v.num AS num,
    v.vdo_type AS vdoType,
    v.play_url AS playUrl,
    LEFT (
    FROM_UNIXTIME(v.publish_time),
    10
    ) AS publishTime,
    v. STATUS AS STATUS,
    v.keywords AS keywords,
    v.coverpic_url AS coverpicUrl,
    v.end_time AS endTime,
    v.start_time AS startTime,
    FROM_UNIXTIME(
    v.update_time,
    '%Y-%m-%d %H:%m:%s'
    ) AS updateTime,
    v.isTop AS isTop,
    parent_id AS parentId,
    p.proid AS proid,
    ifnull(p. NAME, '') AS proName,
    ifnull(p.org, '') AS proOrg,
    ifnull(p.title, '') AS proTitle,
    ifnull(p.department, '') AS proDepartment,
    ifnull(CONCAT(p. NAME, p.title, ' ', p.org), '') AS tempContent
    FROM
    db_yxj_content.t_video v
    LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
    WHERE
    v. STATUS = 0
    AND (
    (
    v.vdo_type = 1
    AND (
    classify_flag = 2
    OR length(v.play_url) > 0
    )
    )
    OR v.vdo_type = 10
    )"

    deltaImportQuery ="SELECT
    CONCAT(v.vdoid,'video') as id,
    v.vdoid AS vdoid,
    v.catid AS catid,
    v.catids AS catids,
    v.proid AS proid,
    v.title AS title,
    ifnull(v.content, '') AS content,
    v.num AS num,
    v.vdo_type AS vdoType,
    v.play_url AS playUrl,
    LEFT (
    FROM_UNIXTIME(v.publish_time),
    10
    ) AS publishTime,
    v. STATUS AS STATUS,
    v.keywords AS keywords,
    v.coverpic_url AS coverpicUrl,
    v.end_time AS endTime,
    v.start_time AS startTime,
    FROM_UNIXTIME(
    v.update_time,
    '%Y-%m-%d %H:%m:%s'
    ) AS updateTime,
    v.isTop AS isTop,
    parent_id AS parentId,
    p.proid AS proid,
    ifnull(p. NAME, '') AS proName,
    ifnull(p.org, '') AS proOrg,
    ifnull(p.title, '') AS proTitle,
    ifnull(p.department, '') AS proDepartment,
    ifnull(CONCAT(p. NAME, p.title, ' ', p.org), '') AS tempContent
    FROM
    db_yxj_content.t_video v
    LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
    WHERE
    v.vdoid = '${dih.delta.id}'"

    deltaQuery ="SELECT
    v.vdoid AS id
    FROM
    db_yxj_content.t_video v
    LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
    WHERE
    v. STATUS = 0
    AND (
    (
    v.vdo_type = 1
    AND (
    classify_flag = 2
    OR length(v.play_url) > 0
    )
    )
    OR v.vdo_type = 10
    )
    AND ( FROM_UNIXTIME(
    v.update_time,
    '%Y-%m-%d %H:%m:%s'
    ) > '${dataimporter.video.last_index_time}'
    OR
    FROM_UNIXTIME(
    v.publish_time,
    '%Y-%m-%d %H:%m:%s'
    ) > '${dataimporter.video.last_index_time}'
    )
    ">
    <field column='publishTime' dateTimeFormat='yyyy-MM-dd' />
    <field column='updateTime' dateTimeFormat='yyyy-MM-dd HH:mm:ss' />
    </entity>

    </document>

    </dataConfig>
    网上对以下字段的解释很多,我这里说下自己的理解:
    query:全量查询执行的sql ,所以一般不需要带上条件
    deltaImportQuery :增量插入,这里需要带上条件,条件的写法很固定,需要通过 deltaQuery 执行后得到的结果 进行数据插入
    deltaQuery :主要服务与deltaImportQuery 在做增量插入的时候,通过该sql 查询出需要增量数据的ID,有两点需要注意,1~这里必须查出ID并且只能叫ID 2~这个sql 的条件的写法也很固定, '${dataimporter.video.last_index_time}' 指的是dataimport.properties 中的 时间,这个时间是指,最后一次执行增量或者全量导入的时间,只要数据发生变化了,dataimport.properties 中的时间会自动更新。

    通过以上三个字段的介绍,应该能明白,solr的增量导入 依据的其实就是,数据库的变化(前提是数据库中需要有update_time 字段,每次数据发生变化要更新该字段,我这里的update_time 是long 类型的unix十进制时间所以写法,需要根据自己实际情况改变)比较最后一次改变数据的时间,做增量数据更改。

    三、一个库 中多个搜索的问题
    实际开发过程中,我们不可能为每一个搜索都创建一个core ,我们的数据大都放到同一个core 中,这样便于维护。那么怎么保证搜索视频的时候不会把文章搜索出来呢。其实很简单,还是看data-config.xml配置如下:
    <dataConfig>
    <!-- <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_studio" user="yxj" password="yxj@2015" /> -->

    <dataSource name ="db_yxj_content" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_content" user="xxx" password="xxx" />
    <dataSource name ="db_yxj_user" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_user" user="xxx" password="xxx" />
    <document name = "insert_test">
    <entity name="video" transformer="DateFormatTransformer"
    query="SELECT
    CONCAT(v.vdoid,'video') as id,
    v.vdoid AS vdoid,
    v.catid AS catid,
    v.catids AS catids,
    v.proid AS proid,
    v.title AS title,
    ifnull(v.content, '') AS content,
    v.num AS num,
    v.vdo_type AS vdoType,
    v.play_url AS playUrl,
    LEFT (
    FROM_UNIXTIME(v.publish_time),
    10
    ) AS publishTime,
    v. STATUS AS STATUS,
    v.keywords AS keywords,
    v.coverpic_url AS coverpicUrl,
    v.end_time AS endTime,
    v.start_time AS startTime,
    FROM_UNIXTIME(
    v.update_time,
    '%Y-%m-%d %H:%m:%s'
    ) AS updateTime,
    v.isTop AS isTop,
    parent_id AS parentId,
    p.proid AS proid,
    ifnull(p. NAME, '') AS proName,
    ifnull(p.org, '') AS proOrg,
    ifnull(p.title, '') AS proTitle,
    ifnull(p.department, '') AS proDepartment,
    ifnull(CONCAT(p. NAME, p.title, ' ', p.org), '') AS tempContent
    FROM
    db_yxj_content.t_video v
    LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
    WHERE
    v. STATUS = 0
    AND (
    (
    v.vdo_type = 1
    AND (
    classify_flag = 2
    OR length(v.play_url) > 0
    )
    )
    OR v.vdo_type = 10
    )">
    <field column='publishTime' dateTimeFormat='yyyy-MM-dd' />
    <field column='updateTime' dateTimeFormat='yyyy-MM-dd HH:mm:ss' />
    </entity>

    <entity name="article" transformer="DateFormatTransformer"
    query="SELECT
    CONCAT(art.artid,'article') as id,
    art.artid as artid,
    art.art_date as artDate,
    art.num as num,
    art.title as artTitle,
    art.brief as brief,
    art.minpic_url as minpicUrl,
    art.content as artContent,
    art.update_time as artUpdateTime
    FROM
    db_yxj_content.t_article art
    WHERE
    art. STATUS = 0">
    <field column='updateTime' dateTimeFormat='yyyy-MM-dd HH:mm:ss' />
    </entity>
    </document>

    </dataConfig>
    如上,可以看到document 下的有多个entity ,其实每个entity 代表的都是不同的数据组。不过虽然数据都被用组分开了,但是查询的时候 还会出现,另外一个问题,那就是 ,通过模糊查询把,文章视频都查了出来,但是我们这次查询其实只是想查询到,视频数据或者文章数据,那么该如何避免该问题呢?
    如下:文章和视频都被查询了出来

    通过为设置copy字段,并创建索引的方式,配置managed-schema 如下:

    <!-- t_video 普通字段不需要建立索引-->
    <field name="vdoid" type="plong" indexed="true" stored="true"/>
    <field name="catid" type="plong" indexed="false" stored="true"/>
    <field name="catids" type="string" indexed="false" stored="true"/>
    <field name="proid" type="plong" indexed="false" stored="true"/>
    <field name="title" type="string" indexed="false" stored="true"/>
    <field name="num" type="pint" indexed="false" stored="true"/>
    <field name="publishTime" type="pdate" indexed="false" stored="true"/>
    <field name="coverpicUrl" type="string" indexed="false" stored="true"/>
    <field name="endTime" type="plong" indexed="false" stored="true"/>
    <field name="updateTime" type="pdate" indexed="false" stored="true"/>
    <field name="content" type="text_general" indexed="false" stored="true"/>
    <field name="playUrl" type="string" indexed="false" stored="true"/>
    <field name="vdoType" type="pint" indexed="false" stored="true"/>
    <field name="startTime" type="plong" indexed="false" stored="true"/>
    <field name="parentId" type="string" indexed="false" stored="true"/>
    <field name="keywords" type="string" indexed="false" stored="true"/>

    <!-- guan lian -->
    <field name="tempContent" type="string" indexed="false" stored="true"/>
    <field name="proName" type="string" indexed="false" stored="true"/>
    <field name="proOrg" type="string" indexed="false" stored="true"/>
    <field name="proDepartment" type="string" indexed="false" stored="true"/>
    <field name="proTitle" type="string" indexed="false" stored="true"/>
    <!-- copyField就引出了solr的一个全文检索的概念,如果我要实现一个搜索,而我要搜索的属性有很多个那么应该使用如下配置方法-->
    <copyField source="title" dest="text_video"/>
    <copyField source="proName" dest="text_video"/>
    <copyField source="proOrg" dest="text_video"/>
    <copyField source="content" dest="text_video"/>
    <!-- 搜索用建立索引 不需要返回-->
    <field name="text_video" type="text_ik" indexed="true" stored="false" multiValued="true" />



    <!-- t_article 普通字段不需要建立索引-->
    <field name="artid" type="plong" indexed="true" stored="true" />
    <field name="artDate" type="string" indexed="false" stored="true"/>
    <field name="artNum" type="pint" indexed="false" stored="true"/>
    <field name="artTitle" type="string" indexed="false" stored="true"/>
    <field name="brief" type="string" indexed="false" stored="true"/>
    <field name="minpicUrl" type="string" indexed="false" stored="true"/>
    <field name="artContent" type="text_general" indexed="false" stored="true"/>
    <field name="artUpdateTime" type="plong" indexed="false" stored="true"/>
    <!-- copyField就引出了solr的一个全文检索的概念,如果我要实现一个搜索,而我要搜索的属性有很多个那么应该使用如下配置方法-->
    <copyField source="artTitle" dest="text_article"/>
    <copyField source="brief" dest="text_article"/>
    <copyField source="artContent" dest="text_article"/>
    <!-- 搜索用建立索引 不需要返回-->
    <field name="text_article" type="text_ik" indexed="true" stored="true" multiValued="true" />
    通过以上配置。可以看到,我把需要索引的字段,通过copyField 的方式 copy 到 新建立的一个field中,如上配置中分别是text_video、text_article,我把这两个file 叫做搜索域,并且根据实际情况设置 type 我用了IK分词器,设置stored 是否返回,为这里只是为了测试设置成true,为了减少开销可以设置为false。并且只对搜索域字段创建索引,可以看到其他字段我都没有创建索引,因为我只会对该字段进行搜索,我觉得这样有两点好处 1.避免不必要的索引开销 2. 规避不需要的域的数据。
    okay~这样基本上,不管是导入还是查询,都能运用到日常的生产开发中了。

    四、 定时创建全量索引和增量索引 ,以及DIH的配置 参数相关
    好,各位有没有发现,如上问题解决完后,还是不能真正的投产,因为,我们不可能通过solr 的管理页面,每次去认为的维护数据,你可能想到了,生产发布前,进行全量导入,之后数据的维护通过定时任务或者,每次对数据增删改查的时候,通过接口对solr进行同步维护。以上肯定可以解决关于数据维护的问题,但是成本太高了,如此优秀的solr 肯定有办法帮你解决这个问题。那就是 solr本身定时器的使用,不过有点繁琐,solr 5以后的版本 定时器的维护也停止了,所以现有的jar包没办法兼容solr7的定时器的使用。需要对jar 进行改造,改造后的jar 可以在如下链接下载:
    https://download.csdn.net/download/csdn_fan321/10222478
    现在说下如何使用该jar包。这里默认你看过我之前写的博客。一下路径都是按照之前的配置来的。
    1.首先需要在E:solrhome 下创建文件夹conf ,并且创建文件dataimport.properties
    写入如下内容,我只用到了 ,增量维护的部分,全量可以定时导入的,具体可以到网上看下,只是配置问题。

    #################################################
    # #
    # dataimport scheduler properties #
    # #
    #################################################

    # to sync or not to sync
    # 1 - active; anything else - inactive
    syncEnabled=1

    # which cores to schedule
    # in a multi-core environment you can decide which cores you want syncronized
    # leave empty or comment it out if using single-core deployment
    # 你的核心 名称,多个用‘,’隔开
    syncCores=testCore

    # solr server name or IP address
    # [defaults to localhost if empty]
    server=localhost

    # solr server port
    # [defaults to 80 if empty]
    # 你得solr 的启动的端口号
    port=8888

    # application name/context
    # [defaults to current ServletContextListener's context (app) name]
    # webapp的名称
    webapp=solr

    # URL params [mandatory]
    # remainder of URL
    # 请求的参数
    params=/dataimport?command=delta-import&clean=false&commit=true&optimize=false&wt=json&indent=on&verbose=false&debug=true

    # schedule interval
    # number of minutes between two runs
    # [defaults to 30 if empty]
    # 执行的频率,分钟
    interval=1


    如上配置,简单易懂,我对请求参数 即DIH的配置 单独讲解下,和页面上复选框其实是一一对应的。
     
    entity:entity是document下面的标签(data-config.xml)。使用这个参数可以有选择的执行一个或多个entity   。使用多个entity参数可以使得多个entity同时运行。如果不选择此参数那么所有的都会被运行。
     clean:选择是否要在索引开始构建之前删除之前的索引,默认为true
     commit:选择是否在索引完成之后提交。默认为true
     optimize:是否在索引完成之后对索引进行优化。默认为true
    debug:是否以调试模式运行,适用于交互式开发(interactive development mode)之中。
    indent:on或者true是打开。false 关闭。返回的数据 是 缩进的。
    2.tomcat 的 webappssolrWEB-INF 下 web.xml ,web-app标签下 添加如下代码:
    <listener>
    <listener-class>org.apache.solr.handler.dataimport.scheduler.ApplicationListener</listener-class>
    </listener>
    3.需要将刚才下载的jar(solr-dataimportscheduler-1.1.0.jar) 放入到.tomcat 的 webappssolrWEB-INFlib 下
    4.重启tomcat ,你能看到

    定时器被执行了,影响的行数为0
    duang~ 搞定

    五、关于ID字段 如何避免同一核中多个搜索避免ID重复的问题
    还有个问题在这里跟大家分享下,就是在solr实际使用中,发现 ID 字段是必须设置的。就是说每个entity 中的数据,都必须有ID ,如果不指定,那么会自动添加UUID 。这个ID 也非常重要,在做数据导入的时候,就是根据ID字段 鉴别记录是否存在,如果存在solr不会反复插入数据,他会在之前的数据上更新替换,这也是 增量导入时候 为了防止数据重复添加的依据,那么问题来了。同一个core 下 多个entity 的情况下,每个entity 都有ID这个字段,那么极有可能出现ID冲突,因为使用mysql的用户大部分都是用的mysql 的自增字段做的ID,那么就会出现在增量更新文章的时候,发现把相同ID的视频记录覆盖了的情况,这是我们必须避免的问题。我采用的是 别名的方式 回避这个问题。其实之前data-config.xml配置文件中的 sql 已经体现了。举例如下:
    正常sql :select id,userName,sex from user;
    优化的sql: select CONCAT(u.id,'user') as id,u.id as uid,u.userName,u.sex from user u;
    这么做就是用 id+标识 当做solr的id使用,如果你需要拿到真的user的id 那就使用uid咯
    ————————————————
    版权声明:本文为CSDN博主「阿胆Amazing」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/wakuangyun/article/details/80684568

  • 相关阅读:
    边缘节点 如何判断CDN的预热任务是否执行完成刷新 路由追踪 近期最少使用算法
    查看恶意登录的尝试账号
    系统启动时发生了什么?
    JMS学习(五)--ActiveMQ中的消息的持久化和非持久化 以及 持久订阅者 和 非持久订阅者之间的区别与联系
    查找最近修改过的文件 并处理
    时间写入文件名 nohup 原理 Command In Background your shell may have its own version of nohup
    nohup COMMAND > FILE
    证明即程序、结论公式即程序类型
    C++学习注意
    C++标准库简介(转)
  • 原文地址:https://www.cnblogs.com/wangwenlong8/p/13021975.html
Copyright © 2020-2023  润新知