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 可以在如下鏈接下載:
現在說下如何使用該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 的 webapps\solr\WEB-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 的 webapps\solr\WEB-INF\lib 下
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咯

六、solr 總結
通過以上的問題總結,我相信,大家都能肆無忌憚的使用solr 了。撒鹽~ 結束!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章