[勾勒一個自己的搜索引擎]ES1初步使用

elastic search初步使用

ElasticSearch是一個基于Lucene的搜索引擎,是當前世界上最受歡迎的全文搜索引擎,其主要特點如下:

  • 橫向可拓展性: 往集群中增加機器時只需要更改一點配置就可以將新機器加入集群
  • 分片機制: 同一個索引切分成不同的分片
  • 高可用: 提供復制集機制,一個分片可以設置多個復制集,某臺機器如果宕機不至于使集群無法工作
  • 使用簡單,基于 REST api就可以完成搜索引擎的全部工作,所需學習成本低。

如無特殊聲明,本文和后續文檔將 Elastic Search 簡稱為ES

全文搜索

全文搜索是指搜索程序掃描整個文檔,通過一定的分詞方法,對每一個詞建立索引,并指明該詞在文檔中出現的位置和次數,最后搜索引擎再通過索引關鍵字搜索出文檔并返回給用戶的過程。

Lucene

Lucene是Apache下的一個開源全文搜索引擎工具,提供了完整的查詢引擎和索引引擎和部分文本分析引擎。不過Lucene僅僅是一個工具包,其目的是為了讓研發人員能夠通過這些工具包快速的為自己的應用搭建一個搜索引擎或者基于這些工具包開發出一個完整的搜索引擎。

倒排序索引

Lucene中的索引采用的是倒排序索引的模式。所謂的倒排序索引(Inverted Index)是通過屬性的值來確定整條紀錄的位置,而不是由紀錄來確定屬性的值。倒排索引把普通索引中的文檔編號和值的關系倒過來,變成:“關鍵詞”對“擁有該關鍵詞的所有文章號”。帶有倒排索引的文件我們稱為倒排索引文件,簡稱倒排文件(inverted file)。倒排索引主要就由索引關鍵字和倒排文件所組成。

建立搜索引擎的關鍵步驟就在于建立倒排索引,倒排索引一般以以下數據結構出現:

關鍵字 文章號[出現頻率] 出現位置
簡單 1[3] 2,9,89
美女 5[1] 1
tip 9[2] 2,7,22

表1 倒排索引數據結構示例

倒排索引在存儲上使用LSM樹,維護其索引方法是當需要新增文檔進入系統時,首先解析文檔,之后更新內存中維護的臨時索引,文檔中出現的每個單詞,在其倒排表列表末尾追加倒排表列表項;一旦臨時索引將指定內存消耗光,即進行一次索引合并,這里需要倒排文件里的倒排列表存放順序已經按照索引單詞字典順序由低到高排序,這樣直接順序掃描合并即可。

實現

lucene在實現倒排索引時將索引劃分為詞典文件(Term Dictionary)、頻率文件(Frequencies)、位置文件(Positions)保存。其中詞典文件不僅包含關鍵詞,還有該關鍵詞指向頻率文件和位置文件的指針。

為了節省存儲空間,提升索引效率。Lucene還對索引進行了壓縮。

安裝

ES使用JAVA語言開發,所以在安裝ES之前需要在系統中安裝有JDK。

表2是本文中的示例所使用的軟件環境信息。

類型 名稱 版本
操作系統 ubuntu server 16.04.2 LTS
內核 Kernel 4.4.0-62-generic
容器 docker 1.28
java openjdk 1.8.0_141
搜索引擎 elasticsearch 5.5.2

表2 本機軟件環境信息

docker鏡像安裝

為了方便部署,我直接采用docker鏡像的方式搭建ES。鏡像啟用命令為:

    $ docker run -d -v "$PWD/esdata":/usr/share/elasticsearch/data --name elasticsearch -H elasticsearch elasticsearch  -Etransport.host=0.0.0.0 -Ediscovery.zen.minimum_master_nodes=1 elasticsearch

系統內核參數

由于elastic search需要用到nio和mmap(虛擬內存映射)技術,在啟動該鏡像之前首先需要檢查一下系統內核對虛擬內存映射數目的限制(vm.max_map_count參數)是否大于262144,如果不滿足這個條件,elastic search鏡像將不會啟動。

我們可以通過以下兩種方式去設置系統內核的vm.max_map_count參數:

    sysctl -w vm.max_map_count=262144

如果想永久修改此參數,可以通過修改 /etc/sysctl.conf 文件的方式使其永久性的成為系統內核參數。

測試

在linux系統下,我們可以使用 curl 程序來完成REST接口的調用與測試,《Elasticsearch權威指南》對 curl 調用REST接口的方式描述如圖1所示。

image

圖一 curl命令示意圖

使用以下命令可以檢測elastic search是否啟動成功。

curl -i -XGET 'localhost:9200/'

-i 參數是說明要打印http請求頭信息,GET是請求方法,localhost:9200/就是我們服務器的地址和端口,elastic search默認與外部交互的端口就是9200.如果服務啟動成功,你會收到類似蝦苗的響應信息。

HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 331

{
  "name" : "Franz Kafka",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "JP677C9kRNqjEWYnFae_gQ",
  "version" : {
    "number" : "5.5.2",
    "build_hash" : "b2f0c09",
    "build_date" : "2017-08-14T12:33:14.154Z",
    "build_snapshot" : false,
    "lucene_version" : "6.6.0"
  },
  "tagline" : "You Know, for Search"
}

使用

為了方便操作,本文與后續內容都將使用chrome瀏覽器的 Restlet Client插件來模擬REST請求,在這里推薦一下,感謝該插件的作者將本插件開源。

創建索引

Elastic Search就像是一個nosql數據庫一樣,存儲我們要進行查詢的信息,在ES中,數據庫名就是索引名。一個 Elastic Search 集群可以包含多個索引,相應的每個索引可以包含多個類型 。這些不同的類型存儲著多個文檔 ,每個文檔又有多個屬性。

我們可以使用PUT方法創建blogs庫,詳細請求信息如圖2所示。

image

圖二 創建blogs數據庫

如果收到以下回應內容,則說明blogs數據庫創建成功。

image

圖3 創建blogs數據庫成功的回應信息

使用GET地址 http://ubuntu:9200/_cat/indices?v可以查看庫的狀態,返回結果如下:

health status index uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   blogs Y9hkRePSQmiAzjNQ_K_FSw   5   1          0            0       810b           810b

其中yellow代表健康度,狀態是活動,索引為blogs,主分片數量5,復制集1,已存儲的文檔數為0.

新建文檔

ES是一個面向文檔的數據庫,每一個文檔都代表一條完整的實體記錄,本文實例中一個文檔就代表一篇文章。存儲一個文檔到ES的行為就叫做索引,在索引一個文檔之前,首先應該明確應該將文檔存儲在哪里。

本文對于數據存儲使用以下設計:

  • 每一篇文章被定義為一個文檔,包含作者,發布時間,文章分類,正文等信息。
  • 每個文檔都屬于articles類型
  • articles類型保存在blogs索引里
  • blogs索引在我們的ES集群中。

按照這個設計思路,我們向ES中索引一篇文章,其請求信息圖4所示。

image

圖4 向blogs索引articles類型插入一篇新文檔

如果新建文檔成功,則會收到HTTP狀態碼為201的回應,回應信息如圖5所示。

image

圖5 向blogs索引articles類型插入一篇新文檔回應信息

接著我們再索引幾篇新文檔,如圖6,圖7.


image

圖6 向blogs索引articles類型插入更多新文檔


image

圖7 向blogs索引articles類型插入更多新文檔。

如果你無法手動對文檔進行編號,可以使用POST方法向ES中索引一個新文檔,其操作方法和鏈接規則如圖8所示。


image

圖8 向blogs索引articles類型POST插入一篇新文檔

從其相應結果中可以看出ES通過自己的規則為文檔增加了_id字段,相應結果如圖9所示.


image

圖9 向blogs索引articles類型POST插入一篇新文檔響應信息

檢索文檔

在ES中檢索已存在的文檔只需使用GET方法訪問剛剛新增文檔時的連接就可以了。

GET http://ubuntu:9200/blogs/articles/2

返回結果如圖10所示。


image

圖10 從blogs索引articles類型檢索文檔

_index屬性代表文檔所屬索引, _type是文檔所屬類型, _id是文檔編號, _version是文檔的版本(每對文檔做一次修改, _version就會加1), _found代表是否查詢到指定文檔, _source就是文檔中所存儲的內容。

更新與刪除文檔

使用REST API對ES中的數據進行修改或刪除也是極其方便的。如果我們要刪除某個文檔,只需使用DELETE方法訪問這個文檔的鏈接就可以了。

DELETE http://ubuntu:9200/blogs/articles/2

我們先介紹一種全量更新文檔的方法,使用PUT方法訪問我們的文檔地址,參數信息加上文檔的新內容就可以全量更新文檔了。文檔更新后,ES會犯規給我們新的文檔版本和操作結果。

示例,對文檔2進行全量更新,其請求與回應信息如圖11,圖12所示。

image

圖11 PUT文檔全量更新


image

圖12 PUT文檔全量更新響應

ES的文檔不能被修改,只能被替換。但ES為我們提供了 update API。通過update API操作從整體來看,我們可以對文檔的某個位置進行部分更新。不過在底層實現上 update API 還是需要進行完整的檢索-修改-重建索引 的處理過程。 區別在于這個過程發生在分片內部,這樣就避免了多次請求的網絡開銷。因為減少了檢索和重建索引步驟之間的時間,更新此文檔時與其他進程的更新操作沖突的可能性也會減少。
update 請求最簡單的一種形式將待更新的字段作為doc的參數, 它只是與現有的文檔進行合并。對象被合并到一起,覆蓋現有的字段或者新增字段。

示例,修改文檔2的author和tags屬性,其請求、新文檔內容如圖13、圖14所示。


image

圖13 POST update文檔部分更新


image

圖14 POST update文檔部分更新后的結果

如果更新文檔是一個并發操作,當前工作在更新文檔的檢索步驟會獲取當前版本號,在重建索引之前會檢查文檔此時的版本號是否與檢索時的版本號一致,如果版本號不一致,當前線程就會發生操作失敗,放棄文檔的修改。如果業務上允許重復更新的話,可以通過retry_on_conflict屬性,來設置失敗重試。該屬性用法如下面代碼所示。對于不能重試的業務場景,update API提供了version屬性進行樂觀并發控制。

    PUT http://ubuntu:9200/blogs/articles/2/_update?retry_on_conflict=5

搜索文檔

搜索是ES的核心,不過鑒于篇幅原因,關于搜索功能的使用將放到下篇文章中。

引用

本文是我在學習使用ES時的筆記,在本文的寫過過程中參考了大量其它資料,有些材料來源于網絡,我由衷的表示感謝,但由于原作者不明,恕不能一一記述。

  1. Elasticsearch 權威指南.——https://www.elastic.co/
  2. Elasticsearch技術解析與實戰/朱林編著.——北京:機械工業出版社,2016.12(數據分析與決策技術叢書)

關于

本項目和文檔中所用的內容僅供學習和研究之用,轉載或引用時請指明出處。如果你對文檔有疑問或問題,請在項目中給我留言或發email到
weiwei02@vip.qq.com 我的github:
https://github.com/weiwei02/ 我相信技術能夠改變世界 。

鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容