關於 ElasticSearch 搜尋引擎
ElasticSearch (簡稱 ES) 是一個分散式搜尋引擎,核心建構在 Apache Lucene 這個 Open Source 的搜尋引擎之上,Lucene 是目前最火熱的全文檢索引擎,作者就是大名鼎鼎的 Doug Cutting,這神人最廣為人知的專案就是 Apache Hadoop 囉。除了 ElasticSearch 其實還有 Apache Solr 也是使用 Lucene 作為核心。奇怪了,那為什麼不直接使用 Lucene 就好了呢?由於 Lucene 函式庫太低階,真的要用很多東西都要自幹才能有比較具體的功能,因此大家都喜歡把 Lucene 包了一層,實作了好用直覺地 REST API,透過抽象的 API 直接操作資料庫,或者像是 ElasticSearch 實作了更容易水平擴充 (Scale-out) 資料庫的分散式架構與叢集,大幅降低進入全文搜尋的門檻。
那麼 ES 是怎麼來的?這又是另一個既悲慘又感人的故事,ES 的作者是 Shay Banon,目前是 ElasticSearch 的創辦人兼 CEO。當年失業的 Shay Banon 跟著妻子來到倫敦,他的妻子在學習廚藝,Shay 為了替他妻子製作食譜搜尋引擎,開始使用 Lucene 進行實作,由於 Lucene 太難用了所以包了一層抽象層,順便發布第一個 Open Source Project 叫做 Compass!沒錯,你想到的 CSS Compass 不是這一個 Compass!後來 Shay 大師找到了一份分散式系統相關的工作,隨著分散式搜尋的需求,後來改寫 Compass 成為現在的 ElasticSearch,開啟了 Open Source 分散式搜尋引擎的光明大路。但是,至今...... Shay 的妻子依然沒有拿到他的食譜搜尋引擎.......這故事好像也發生在我身上,我是說欠別人系統沒完成這件事,不是當上 CEO.......XD
ElasticSearch 索引概念與運作原理
ES 也是 NoSQL 資料庫的一種,所有的資料都是以 JSON 的方式進行存取,包含設定也是一樣。用起來像是 MongoDB 這樣的 Document DB,但最大的不同是所有欄位都可以建立索引進行全文搜尋,目前 MongoDB 只有對部分語言支援全文搜尋,像是中文目前就尚未支援。
簡略介紹一下 ElasticSearch 的元素,典型的資料結構 URI 為 /index/type/id,可以透過 Rest API 對資料進行操作,比如想擷取資料 API 就呼叫 [GET] http://localhost:9200/index/type/id 即可。最頂層為 index 如同關聯式資料庫中的「Database」,第二層是 type,比較像是關聯資料庫中的「Table」或 MongoDB 中的 Collection 角色,最後的 id 就如同 Primary Key,對資料的修改與刪除都需要這些元素所組成的 URI。
我們已經知道 ElasticSearch 資料庫是以 JSON Document 存放,但是實際的資料會放在分割的 Shard 並且存放在不同的 ElasticSearch Data Node 中,在 Cluster 架構上我們可以設定「資料複本」的數量,執行時 ElasticSearch 會自動將 Shard 分配到不同的 Node。並且在 Node Fail Revocery 或 New Node 時會自動分散 Shard 資料到其他節點中,聽說 ElasticSearch 可以輕易的水平擴展到上千台節點,去中心化架構可以同時分散運算與資料存放,聽起來超威的啦,用來處理巨量資料非常好用,這一點倒是贏了 MongoDB。
那麼全文檢索到底是怎實現的?全文索引與一般資料庫的索引有什麼不同?全文搜尋引擎使用的索引方式叫做 Inverted Index (反向索引) 演算法。反向索引是目前全文檢索中最常被使用的技術,底層的 Lucene 也是透過實作反向索引來實現全文檢索 (官方介紹)。
廢話說完了看一下怎安裝,以下安裝測試環境為 Ubuntu Server 14.04 TLS,讓我手把手進行吧。
安裝 Oracle Java
這幾年雲端相關 Open Source Project 最愛用 Java 了,所以先裝一下 Java 吧。
sudo add-apt-repository -y ppa:webupd8team/java
sudo apt-get update
sudo echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections
sudo echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections
sudo apt-get -y install oracle-java8-installer
安裝 ElasticSearch Cluster 5.4.3
ElasticSearch 版本發布很快,寫這篇文章的時候已經出到 5.5.0 了,但是由於我們等等要測試的「中文分詞 IK Plugin」還沒跟上最新版,因此先裝 5.4.3 順便進行一些 543 的測試。最新版本可以在官方的下載頁面取得。
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.3.deb
sudo dpkg -i elasticsearch-5.4.3.deb
sudo update-rc.d elasticsearch start 20 2 3 4 5 . stop 80 0 1 6 .
sudo service elasticsearch restart
服務啟用後會開啟 TCP 9200, 9300 這兩個 Port,9200 是 API WebService,9300 是在 Cluster 環境下讓節點之間傳遞資料的服務。ElasticSearch 成功啟用後可以透過瀏覽器連接以下位置 http://localhost:9200/_cluster/health 取得運作資訊。回應如下:
{ cluster_name:"elasticsearch", status:"yellow", timed_out:false, number_of_nodes:1, number_of_data_nodes:1, active_primary_shards:16, active_shards:16, relocating_shards:0, initializing_shards:0, unassigned_shards:16, delayed_unassigned_shards:0, number_of_pending_tasks:0, number_of_in_flight_fetch:0, task_max_waiting_in_queue_millis:0, active_shards_percent_as_number:50 }
上述資訊最重要的就是 status,yellow 表示資料完整,但是沒有另一份副本存在,系統可以正常服務,由於我們只有裝一台,所以「黃燈」是正常的。green 就表示 Cluster 模式運作正常,設定的副本量都有正確分佈在其他節點中。red 就表示 GG 惹,在紅燈的情況下 API 是無法進行服務的,表示 Active Primary Shard 在 Cluster 中有遺漏,可能有節點掛了或者還沒上線,透過 shards 相關資訊可以得知詳細狀態。
由於 ElasticSearch 沒有處理認證這件事,因此預設僅允許透過 127.0.0.1 訪問服務,如果要架設 Cluster 或者開啟外部主機進行 API 訪問,可以修改 /etc/elasticsearch/elasticsearch.yml 這支檔案中的「network.host」設定,大無敵全開放的方法如下:
開啟對外連線 (危險喔,請謹慎使用)
sudo sed -i "s/#network.host: 192.168.0.1/network.host: 0.0.0.0/g" /etc/elasticsearch/elasticsearch.yml
sudo service elasticsearch restart
安裝 ElasticSearch IK Analysis Plugin
由於我需要利用 ES 進行中文全文搜尋,預設 ES 僅接受 UTF-8 編碼的資料,對於中文的分詞是一個字一個字切割,什麼事都不做雖然一樣可以搜尋中文,但是由於無法分詞,就少了一些好用的容錯功能 (比如字詞糾正、模糊搜尋),搜尋文章的效果比較差。ELasticSearch 可以透過外掛來擴充功能,因此我們需要加上額外的中文分詞外掛,IK Plugin 就是在做這一件事,可以像是英文一樣把單組成詞,這樣一來透過詞進行搜尋,分數就會比較漂亮。上面的動作先安裝好 ES 之後就可以繼續安裝外掛,記得外掛的版本要與 ElasticSearch 相同最沒有問題,安裝方法如下:
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.4.3/elasticsearch-analysis-ik-5.4.3.zip
sudo mkdir /usr/share/elasticsearch/plugins/ik
sudo mv elasticsearch-analysis-ik-5.4.3.zip /usr/share/elasticsearch/plugins/ik
cd /usr/share/elasticsearch/plugins/ik/
apt install -y unzip
sudo unzip elasticsearch-analysis-ik-5.4.3.zip
sudo rm elasticsearch-analysis-ik-5.4.3.zip
sudo service elasticsearch restart
完成以後來測試看看,但由於 ElasticSearch 唯一的使用方法就是呼叫 API,一般透過 CURL 套件就可以完成所有事情。但我們不這麼做,為了以後方便除錯與調閱 Log 資料,先安裝 Kibana 來操作 ES,先來看看怎麼安裝吧。
安裝 Kibana 5.4.3
Kibana 其實我不知道算是什麼類型的系統,應該會是視覺化報表系統吧!?反正很酷炫就是了,可以看看官方網站的介紹,可以透過 ElasticSearch 資料庫建立很多很漂亮的報表。安裝方法如下:
wget https://artifacts.elastic.co/downloads/kibana/kibana-5.4.3-amd64.deb
sudo dpkg -i kibana-5.4.3-amd64.deb
sudo update-rc.d kibana start 20 2 3 4 5 . stop 80 0 1 6 .
sudo service kibana restart
裝好啟動以後會開啟 5601 Port,開啟瀏覽器進入 http://localhost:5601 ,如果 Kibane 正常連線到 Elastic Search 進入後會看到以下畫面,第一次裝好會需要先設定並指定讓 Kibana 進行分析的 index,我們這裡先跳過,等等建完資料再回來設定。
先點擊左邊選單的「Dev Tools」功能,這是一個好用方便的除錯測試工具,在 Console 輸入以下命令進行測試:
- 建立 Index
- 設定 Mapping
- 建立測試資料
- 用「後悔莫及」關鍵字測試搜尋結果
PUT my-index POST /my-index/fulltext/_mapping { "fulltext": { "_all": { "analyzer": "ik_max_word", "search_analyzer": "ik_max_word", "term_vector": "no", "store": "false" }, "properties": { "content": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_max_word", "include_in_all": "true", "boost": 8 } } } } POST my-index/fulltext/1 {"content":"曾經有一份真摯的感情放在我面前.我沒有珍惜.等到失去的時候才後悔莫及,塵世間最痛苦的事莫過於此.你的劍在我的咽喉上割下去吧!不要再猶豫了!如果上天能夠給我一個再來一次的機會,我一定會對那個女孩子說三個字\"我愛你\",如果非要在這份愛上加一個期限的話,我希望是一萬年。"} POST my-index/fulltext/_search { "query" : { "match" : { "content" : "後悔莫及" }}, "highlight" : { "pre_tags" : ["<tag1>", "<tag2>"], "post_tags" : ["</tag1>", "</tag2>"], "fields" : { "content" : {} } } }
執行結果如下:
透過回傳的 Highlight 我們可以發現 Match 的方式並不是很完整詞,「後悔莫及」結果分割為「後、悔、莫及」,是因為 IK 內建的字典檔不夠豐富,沒有包含「後悔莫及」這個詞,當然我們可以自行擴充字典檔。IK Plugin 字典檔放在 /usr/share/elasticsearch/plugins/ik/config/main.dic 這個路徑,可以自行更換。為了獲得更好的搜尋效果,我們先更換為「超齊百萬字典檔」,這是之前特地製作的字典檔,內容包含 UTF-8 繁體與簡體字碼 (目前有 11x 萬詞),有需要的請自行下載使用。替換以後重新啟動服務 (IK 也支援熱字典檔擴充),再用一樣的關鍵字搜尋看看,結果如下:
由上面的結果我們可以看到「後悔莫及」四個字已經正確被切分,有興趣的可以體驗看看。如果想要直接使用 Kibana 進行資料查詢,需要先設定 index,點選 Kibana 左側的「Management」如下:
設定後就可以看到抓到目前資料的 Column,如下:
Kibana 我其實也還不太熟悉,沒辦法多做什麼介紹囉。
ElasticSearch + Kibana + IK Plusin + 超齊百萬字典檔 Docker
上述的安裝步驟實在太多了,如果你想要使用 Docker 安裝的話,我有把上述過程包成 Docker,安裝方法如下:
sudo docker pull samejack/docker-elasticsearch-kibana:latest
sudo docker run -p 5601:5601 -p 9200:9200 -it samejack/elasticsearch-kibana:latest
ElasticSearch 我最近主要用在全文搜尋與 Log Server,用起來還算是蠻方便的,查詢速度都是毫秒級可以完成,記憶體比較吃重。如果各位對 ElasticSearch 核心的分散式運作機制有興趣,可以閱讀官方的 The Definitive Guide 電子書 (寫這篇文章的時候我也還在閱讀中...),其中有比較深入的介紹,今天就先講到這,Bye 囉.......