9、Solr 檢索工具
1、Solr 簡介
Solr 是一個高效能,基於 Lucene 的全文檢索服務,也可以作為 NoSQL 資料庫使用。Solr 對 Lucene 進行了擴充套件,提供了比 Lucene 更為豐富的查詢語言,同時實現了可配置、可擴充套件,並對查詢效能進行了優化,還提供了一個完善的功能管理介面。
SolrCloud 從 Solr 4.0 版本基於 Solr 和 Zookeeper 進行開發,具有開創意義的分散式索引和搜尋方案。
應用場景:
待檢索的資料型別複雜:如需要查詢的資料有結構化資料(關係型資料庫等)、半結構化資料(網頁、XML 等)、非結構化資料(日誌、圖片、影象等)等,Solr可以對以上資料型別進行清洗、分詞、建立倒排索引等一系列操作(建立索引),然後提供全文檢索(查詢)的能力。
檢索條件多樣化(如涉及欄位太多),常規查詢無法滿足:全文檢索(查詢)可以包括詞和短語,或者詞或短語的多種形式。
讀資料取遠多於寫入資料的應用場景。
在實際生活中,我們也需要使用到大量的檢索服務才可以正常的執行相關的工作,比如春運買票,作為 12306 的系統,我們在買票的時候,後臺需要首先檢索出我們的資料,之後檢查我們的購票記錄,然後針對於我們的購票行為還要新增新的 資料到資料庫中。那麼在 12306 後臺資料庫中,使用者十幾億,如何快速的檢索出一條資料這就是我們所需要關注的問題了。
Solr 提供的功能分為:索引和查詢。
索引資料涉及的內容:資料來源、索引儲存以及索引資料匯入方式。
(1)資料來源可以從 DB、HBase、HDFS 等元件的資料匯入而來。
(2)索引儲存方式 可以選擇為儲存於本地或者 HDFS。
(3)索引方式 可以採用 MR 的任務進行批量匯入及使用客戶端 API 實時匯入。在關係資料庫中,索引是一種單獨的、物理的對資料庫表中一列或多列的值進行排序的一種儲存結構,它是某個表中一列或若干列值的集合和相應的指向表中物理標識這些值的資料頁的邏輯指標清單。索引的作用相當於圖書的目錄,可以根據目錄中的頁碼快速找到所需的內容。索引提供指向儲存在表的指定列中的資料值的指標,然後根據您指定的排序順序對這些指標排序。資料庫使用索引以找到特定值,然後順指標找到包含該值的行。這樣可以使對應於表的 SQL 語句執行得更快,可快速訪問資料庫表中的特定資訊。當表中有大量記錄時,若要對錶進行查詢,第一種搜尋資訊方式是全表搜尋,是 將所有記錄一一取出,和查詢條件進行一一對比,然後返回滿足條件的記錄,這 樣做會消耗大量資料庫系統時間,並造成大量磁碟 I/O 操作;第二種就是在表中建立索引,然後在索引中找到符合查詢條件的索引值,最後通過儲存在索引中的 ROWID(相當於頁碼)快速找到表中對應的記錄。
在實際的操作中,我們如果需要做到快速檢索資料,首先第一步需要做到的就是建立索引,之後才能做對應的資料搜尋工作。查詢涉及的內容主要為:顯示方式以及語法。查詢顯示方式可以是通過 Hue、Custom UI 或者 Custom App 來實現。
索引的建立是為了能夠更好更快的查詢相關的資料,在查詢的時候,我們可以通過多種方式進行資料的查詢工作,比如通過 shell 語句進行查詢,比如通過相關的 UI 介面進行查詢等,查詢又分為了多種形式的查詢,比如全文查詢(從當前所有的資料中進行遍歷式的查詢操作)、關鍵字查詢以及條件篩選查詢等。
2.Solr 總述
在 Solr 中,我們主要關注上圖中的幾個元件:
(1)ConfigSet:是 Solr 的工作配置檔案,該檔案指導了 Solr 的工作方式,索引以及相關對應的資料檔案的儲存路徑,和在實際工作中的一些引數值(2)SolrCloud:SolrCloud 相當於是程序一端的最高程序,通過 SolrCloud,
我們可以建立 Solr 例項,Solr 的 Core 核心,也就是指 SolrCloud 是 Solr中的總程序,我們通過 Solr 的總程序可以控制 Solr 例項的建立、刪除、故障的切換等一系列的關於程序的執行操作,SolrCloud 具有 Solr 的最高執行許可權。
(3)Solr:Solr 例項就是 SolrCloud 在進行工作時的建立的一個個針對於不同的應用的實際工作程序,所謂說例項,就是指類的一個具體的範疇,比如鳥類就是一個類別,孔雀就是鳥類中的一個實際的例子,所以孔雀就作為了一個鳥類的例項。所以在 Solr 中,我們建立的實際的索引程序都可以稱之為是 Solr 中的一個例項。
(4)Core:Core 相當於是一個執行處理引擎,我們傳送的資料的查詢檢索請求實際上都是通過 Core 的執行來進行操作的,Core 獲取到使用者的請求, 然後根據使用者的請求轉化為對應的執行動作,然後 Core 會去查詢或者檢索資料,將執行的結果進行統一的整合,最終傳送給請求者。所以實際來說,Core 相當於是 Solr 中的整體處理核心發動機。
在 Solr 中,我們實際上是分為了控制的實際程序和資料的邏輯程序的,那麼上面所說的所有程序都屬於是實際存在於 Solr 中的維護和控制程序。 SolrCloud 根據 ConfigSet 中的控制檔案,來進行實際的工作,建立並且維護 Solr 例項,然後 Solr 例項中,如果是一個單程序對資料進行相關的維護,那麼勢必會造成壓力過大的情況產生,所以在這個時候,我們就需要將實際的處理程序按照分散式的思想進行切分,通過多程序同時維護就可以保障業務的壓力被均分到各個節點上,所以我們在 Solr 例項中建立了 Core 程序來進行實際的工作和資料的維護操作。
那麼下面我們所說的就是 Solr 中對於資料的切分維護所產生的邏輯資料程序。(1)對於 Solr 來說,首先資料在 Solr 上也是一個整體的完整索引邏輯存在,我們將這個完整的 solr 索引稱為 Collection。
(2)Collection 由於是一個完整的邏輯索引整體,就像之前所說的一樣,我們需要做的就是引入分散式的概念,將邏輯資料整體切分給不同的裝置去進行維護,那麼在這裡首先我們先將 Collection 切分為第二級的小邏輯概念,也就相當於是將一個完整的邏輯索引切分為很多的分片,我們稱之為是 Shard。如果Collection 不夠大,那麼可能會出現一個 Shard 就是一個 Collection 的情況。
(3)我們一般會將 Collection 切分為 shard,之後 shard 就不會再做對應的切分了,因為我們需要保護資料的安全,不會因為故障而丟失或者是損壞, 那麼這裡我們就又進一步借鑑了整體 Hadoop 的設計理論了,採用的是資料副本的形式來進行保護,我們會將 Shard 中的資料建立副本,一個副本就稱為一個Replica,由於我們對於索引更多的是讀取操作,所以為了保證資料讀取的高效性,我們將這些副本都使用到了,而不是像 HDFS 中一樣資料副本做備份,在 Solr 中所有的 Replica 都是被使用的,他們提供了相關的讀取和查詢服務,但是現在就出現了一個問題,就是 Replica 中的資料都是相同的,這個情況下假如某一個節點更改了資料,那麼這個時候其他節點如果獲取不到更改的資訊,就會造成節點和節點之間的資料不同步的問題了。為了解決這個問題,我們在 Replica 這個層級添加了一個新的角色叫做 leader,Leader 其實就是一個許可權更高的 Replica,實質上和 Replica 沒有區別。我們將一個 shard 的多副本資料稱之為是一個組,那麼一個組內就會存在多個 Replica,這些 Replica 通過選舉的方式選出一個 Leader,當收到一個讀取請求的時候,由於不會對現有的資料造成什麼影響, 所以只讀條件下的正常讀取是沒有問題的, 每一個 Replica 都可以去執行讀和查詢的操作,但是一旦涉及到寫操作,這個時候就會改變全域性所有組內 Replica 的資料,這個時候如果某一個 Replica 收到寫請求之後,就必須將請求轉發給 Leader 去執行。
3.邏輯檢視
就像之前說的那樣,我們的程序和資料是相互關聯的,Core 作為實際進行工作的程序,關聯的也是資料層面提供業務的程序,所以這裡 Core 是關聯到 Replica的。如上圖所示,這裡 SolrCloud、Solr、SolrCore 是實際程序,Replica 是儲存實際的資料,shard 和 Collection 實際上都是以邏輯程序存在的。
4.SolrCloud 倒排索引
傳統的搜尋方式(正排序索引)是從關鍵點出發,然後再通過關鍵點找到關鍵點代表的資訊中能夠滿足搜尋條件的特定資訊,即通過 KEY 尋找 VALUE。通過正排序索引進行搜尋,就是從通過文件編號找關鍵詞。
Solr(Lucene)的搜尋採用了倒排序索引的方式,即通過 VALUE 找 KEY。而在中文全文搜尋中 VALUE 就是我們要搜尋的關鍵詞,存放所有關鍵詞的地方叫詞典。KEY 是文件標號列表(通過文件標號列表我們可以找到出現過要搜尋關鍵詞 --VALUE 的文件),具體如下面的圖所示:通過倒排索引進行搜尋,就是通過關鍵詞查詢對應的文件編號,再通過文件編號找文件,類似於查字典,或通過查書目錄查指定頁碼書的內容。
具體的來解析傳統索引和倒排索引的問題,我們可以用上面的例子來看,傳統的正排索引就類似於是查英文字典,我們需要進行全文的查詢,然後匹配我們搜尋的 key 值,也就相當於是當我們需要查詢一個數據的時候,我們是通過遍歷資料的形式去做匹配的,但是倒排索引我們就需要先建立一個字典,通過字典中記錄的關鍵詞去查詢資料,就像是中文的字典中有目錄索引一樣。我們通過索引直接找到對應的資料。
分散式索引流程
①當 Client 發起一次文件索引請求時,首先將從 zookeeper 叢集中獲取
SolrCloud 中 SolrServer 的叢集資訊,根據請求中的 collection 資訊,獲取任意一臺包含該 collection 資訊的 SolrServer;②然後 Client 把文件索引請求傳送給 SolrServer 中該 collection 對應 shard中的一個 Replica 進行處理;
③如果該 Replica 不是 LeaderReplica,則該 Replica 會把文件索引請求再轉發給和自己相同 shard 中相對應的 LeaderReplica;
④該 LeaderReplica 在本地完成文件的索引後,會再把文件索引請求路由給本Shard 中的其他 Replica 進行處理;
⑤如果該文件索引的目標 shard 並不是本次請求的 Shard,那麼該 Shard 的 LeaderReplica 會將文件索引請求再次轉發給目標 Shard 的 LeaderReplica;
⑥目標 Shard 的 LeaderReplica 在本地完成文件的索引後,會再把文件索引請求再次路由給本 Shard 的其他 Replica 進行處理。
這裡的索引流程中,多 Replica 是為了保證 Solr 服務的可靠性,當有 replica 掛掉時,一個 shard 上還有其他 replica 可以工作。但是 replica 複本數增多時候,其索引階段需要從 LeaderReplica 同步到其他 Replica 的過程增多,索引效能便會下降。建議一般在生產環境,Replica 設定為 2,即每個 Shard 上有兩個Replica。每個索引進入到相應的 Shard 上時,都需要經過 Leader 進行處理,這樣是為了保證 Shard 上資料的一致性,由 Leader 將請求轉發給同一 Shard 上的其他 Replica。
分散式搜尋流程
SolrCloud 分散式搜尋流程
①當 Client 發起一次搜尋請求時;Client 首先將通過 zookeeper 會獲取到 SolrServer 伺服器叢集資訊,並隨機選取一個含有該 collection 的SolrServer;②然後 Client 把搜尋請求傳送到該 Collection 在 SolrServer 上對應Shard 中的任意一個 Replica(可以不為 Leader Replica)進行處理;
③該 Replica 再根據查詢索引的方式,啟動分散式查詢,基於 Collection 的 Shard 個數(在上圖中為 2 個-Shard1 和 Shard2),把查詢轉換為多個子查詢,並把每個子查詢分發到對應 Shard 的任意一個 Replica(可以不為 Leader Replica)中進行處理;
④每個子查詢完成查詢操作後,並查詢結果返回;
⑤首次收到查詢請求的 Replica 收到各個子查詢的查詢結果後,對各個查詢結果進行合併處理,然後把最終的查詢結果返回給 Client。在查詢時候,請求是隨意發到一個 replica,並非需要 leader 進行處理,這樣當 replica 資料增多時,併發查詢下,Solr 的查詢效能加快。
5.Solr 索引 HBase 資料
Solr 索引 HBase 資料是將 HBase 資料寫到 HDFS 的同時,Solr 建立相應的 HBase索引資料。其中索引 id 與 HBase 資料的 rowkey 對應,保證每條索引資料與 HBase 資料的唯一,實現 HBase 資料全文檢索。
HBaseIndexer 是 Solr 索引 HBase 資料的工具,主要功能有批量索引和實時索引批量索引通過建立 MR 任務,將匯入 HBase 內的資料,在 Solr 中建立索引。批量索引可以基於 scan 方式將全表資料全部掃描一遍,再在 Solr 中建立索引;也可使用 Loader 增量匯入 HBase 表的過程中生成的 rowkey 列表,再提交 MR 任務進行增量索引;實時索引則是基於 HBase 的 replication 機制,在資料匯入 HBase 的同時,在 Solr 內建立索引,但是效率相比批量索引,增量索引會差一些。
批量索引:
在 HBase 表中在已有大量資料的情況下,可以使用 HBaseIndexer 提交 MR 任務進行批量索引。
(1)讀取 HBase 表獲取 Region 資訊,劃分 Split
(2)掃描每個 split 對應的表資料,每個 reduce 任務生成本地索引檔案(3)將索引檔案 merge 到 Solr
實時索引:
對 HBase 資料實時索引,實時錄入 HBase 資料的同時,使用 SolrjAPI 建立索引。
(1) 業務應用程式(sparkstreaming、storm 任務等),從 Kafka 訊息佇列中拉取資料。
(2) 使用 HBaseClientAPI 寫入資料到 HBase 表
(3) 使用 SolrjAPI 寫入資料到 Solr
6.Solr 索引 HDFS 資料
SolroverHDFS 主要利用 MapReduce 框架讀取 HDFS 上的各種非結構化資料(doc、xml、txt、csv 等),在 SolrCloud 中建立索引檔案,同時將索引檔案儲存在 HDFS 上。
特點:對於大量非結構化資料的全文檢索,建立索引速度,處理資料量大,索引儲存量大。
在 HDFS 上已有大量資料情況下,可以使用 HDFSIndexer 來批量索引。HDFSIndexer通過建立 MR 任務,掃描 HDFS 上指定目錄下的結構化檔案,採用 Merge index 檔案方法向 Solr 建立索引。
7.參考文獻
小時候我們都使用過新華字典,媽媽叫你翻開第 38 頁,找到“坑爹”所在的位置,此時你會怎麼查呢?毫無疑問,你的眼睛會從 38 頁的第一個字開始從頭至尾地掃描,直到找到“坑爹”二字為止。這種搜尋方法叫做順序掃描法。對於少量的資料,使用順序掃描是夠用的。但是媽媽叫你查出坑爹的“坑”字在哪一頁時,你要是從第一頁的第一個字逐個的掃描下去,那你真的是被坑了。此時你就需要用到索引。索引記錄了“坑”字在哪一頁,你只需在索引中找到“坑”字,然後找到對應的頁碼,答案就出來了。因為在索引中查詢“坑”字是非常快的,因為你知道它的偏旁,因此也就可迅速定位到這個字。
那麼新華字典的目錄(索引表)是怎麼編寫而成的呢?首先對於新華字典這本書來說,除去目錄後,這本書就是一堆沒有結構的資料集。但是聰明的人類善於思考總結,發現每個字都會對應到一個頁碼,比如“坑”字就在第 38 頁,“爹”字在第 90 頁。於是他們就從中提取這些資訊,構造成一個有結構的資料。類似資料庫中的表結構:
wordpage_no
--------------
無 38
語 90
......
這樣就形成了一個完整的目錄(索引庫),查詢的時候就非常方便了。對於全文檢索也是類似的原理,它可以歸結為兩個過程:1.索引建立(Indexing)2.搜尋索引(Search)。那麼索引到底是如何建立的呢?索引裡面存放的又是什麼東西呢?搜尋的的時候又是如何去查詢索引的呢?帶著這一系列問題繼續往下看。
索引
Solr/Lucene 採用的是一種反向索引,所謂反向索引:就是從關鍵字到文件的對映過程,儲存這種對映這種資訊的索引稱為反向索引
- 左邊儲存的是字串序列
- 右邊是字串的文件(Document)編號連結串列,稱為倒排表(PostingList)欄位串列表和文件編號連結串列兩者構成了一個字典。現在想搜尋”lucene”,那麼索引直接告訴我們,包含有”lucene”的文件有:2,3,10,35,92,而無需在整個文件庫中逐個查詢。如果是想搜既包含”lucene”又包含”solr”的文件,那麼與之對應的兩個倒排表去交集即可獲得:3、10、35、92。
索引建立假設有如下兩個原始文件:
文件一:Students should be allowed to go out with their friends, but not allowed to drink beer.
文件二:My friend Jerry went to school to see his students but found them drunk which is not allowed.
建立過程大概分為如下步驟:
一:把原始文件交給分片語件(Tokenizer)
分片語件(Tokenizer)會做以下幾件事情(這個過程稱為:Tokenize),處理得到的結果是詞彙單元(Token)
- 將文件分成一個一個單獨的單詞
- 去除標點符號
- 去除停詞(stopword)
所謂停詞(Stopword)就是一種語言中沒有具體含義,因而大多數情況下不會作為搜尋的關鍵詞,這樣一來建立索引時能減少索引的大小。英語中停詞(Stopword)如:”the”、”a”、”this”,中文有:”的,得”等。不同語種的分片語件
(Tokenizer),都有自己的停詞(stopword)集合。經過分詞(Tokenizer)後得到的結果稱為詞彙單元(Token)。上例子中,便得到以下詞彙單元(Token):"Students","allowed","go","their","friends","allowed","drink","beer","My","friend","Jerry","went","school","see","his","students","found","them","drunk","allowed"
二:詞彙單元(Token)傳給語言處理元件(LinguisticProcessor)
語言處理元件(linguisticprocessor)主要是對得到的詞元(Token)做一些語言相關的處理。對於英語,語言處理元件(LinguisticProcessor)一般做以下幾點:
- 變為小寫(Lowercase)。
- 將單詞縮減為詞根形式,如”cars”到”car”等。這種操作稱為:stemming。
- 將單詞轉變為詞根形式,如”drove”到”drive”等。這種操作稱為:lemmatization。
語言處理元件(linguisticprocessor)處理得到的結果稱為詞(Term),例子中經過語言處理後得到的詞(Term)如下:
"student","allow","go","their","friend","allow","drink","beer","my","friend","jerry","go","school","see","his","student","find",
"them","drink","allow"。
經過語言處理後,搜尋 drive 時 drove 也能被搜尋出來。 Stemming 和 lemmatization 的異同:
相同之處:
Stemming 和 lemmatization 都要使詞彙成為詞根形式。不同之處:兩者的方式不同:
(1) Stemming 採用的是”縮減”的方式:”cars”到”car”,”driving”到”drive”。
(2) Lemmatization 採 用 的 是 ” 轉 變 ” 的 方 式 : ”drove”到”drove”,”driving”到”drive”。
兩者的演算法不同:
(1) Stemming 主要是採取某種固定的演算法來做這種縮減,如去除”s”,去
除 ”ing” 加 ”e” ,將 ”ational” 變為 ”ate” ,將 ”tional” 變為”tion”
(2) Lemmatization 主要是採用事先約定的格式儲存某種字典中。比如字典中有”driving”到”drive”,”drove”到”drive”,”am,is,are”
到”be”的對映,做轉變時,按照字典中約定的方式轉換就可以了。
(3) Stemming 和 lemmatization 不是互斥關係,是有交集的,有的詞利用這兩種方式都能達到相同的轉換。
三:得到的詞(Term)傳遞給索引元件(Indexer)
(1)利用得到的詞(Term)建立一個字典 Term DocumentID
student |
1 |
|
allow |
1 |
|
go |
1 |
|
their |
1 |
|
friend |
1 |
|
allow |
1 |
|
drink |
1 |
|
beer |
1 |
|
my |
2 |
|
friend |
2 |
|
jerry |
2 |
|
go |
2 |
|
school |
2 |
|
see 2 |
||
his 2 |
||
student |
2 |
|
find |
2 |
|
them |
2 |
|
drink |
2 |
|
allow |
2 |
(2)對字典按字母順序排序: |
||
Term |
Document ID allow |
1 |
allow |
1 |
allow |
2 |
beer |
1 |
drink |
1 |
drink |
2 |
find |
2 |
friend |
1 |
friend |
2 |
go |
1 |
|
go |
2 |
|
his 2 |
||
jerry |
2 |
|
my |
2 |
|
school 2 |
||
see 2 |
||
student |
1 |
|
student |
2 |
|
their |
1 |
|
them |
2 |
3.合併相同的詞(Term)成為文件倒排(Posting List)連結串列
Document Frequency:文件頻次,表示多少文件出現過此詞(Term) Frequency:詞頻,表示某個文件中該詞(Term)出現過幾次對詞(Term)“allow”來講,總共有兩篇文件包含此詞(Term),詞(Term)後面的文件連結串列總共有兩個,第一個表示包含”allow”的第一篇文件,即 1 號文件,此文件中,”allow”出現了 2 次,第二個表示包含”allow”的第二個文件,是2 號文件,此文件中,”allow”出現了 1 次至此索引建立完成,搜尋”drive”時,”driving”,”drove”,”driven”也能夠被搜到。因為在索引中,”driving”,”drove”,”driven”都會經過語言處理而變成”drive”,在搜尋時,如果您輸入”driving”,輸入的查詢語句同樣經過分片語件和語言處理元件處理的步驟,變為查詢”drive”,從而可以搜尋到想要的文件。搜尋步驟搜尋”microsoftjob”,使用者的目的是希望在微軟找一份工作,如果搜出來的結果是:”Microsoft does a good job at software industry…”,這就與使用者的期望偏離太遠了。如何進行合理有效的搜尋,搜尋出使用者最想要得結果呢?搜尋主要有如下步驟:
一:對查詢內容進行詞法分析、語法分析、語言處理
- 詞法分析:區分查詢內容中單詞和關鍵字,比如: englishand ,janpan,”and”就是關鍵字,”english”和”janpan”是普通單詞。
- 根據查詢語法的語法規則形成一棵樹
- 語言處理,和建立索引時處理方式是一樣的。比如:leaned–>lean, driven–>drive
二:搜尋索引,得到符合語法樹的文件集合三:根據查詢語句與文件的相關性,對結果進行排序
我們把查詢語句也看作是一個文件,對文件與文件之間的相關性(relevance)進行打分(scoring),分數高比較越相關,排名就越靠前。當然還可以人工影響打分,比如百度搜索,就不一定完全按照相關性來排名的。
如何評判文件之間的相關性?一個文件由多個(或者一個)詞(Term)組成,比如:”solr”, “toturial”,不同的詞可能重要性不一樣,比如 solr 就比 toturial 重要,如果一個文件出現了 10 次 toturial,但只出現了一次 solr,而另一文件 solr 出現了 4 次,toturial 出現一次,那麼後者很有可能就是我們想要的搜的結果。這就引申出權重(Term weight)的概念。權重表示該詞在文件中的重要程度,越重要的詞當然權重越高,因此在計算文件相關性時影響力就更大。通過詞之間的權重得到文件相關性的過程叫做空間向量模型演算法(Vector Space Model)