本篇主要內容如下:
前言
項目中我們總是用 Kibana
界面來搜索測試或生產環境下的日志,來看下有沒有異常信息。Kibana
就是 我們常說的 ELK
中的 K
。
(資料圖片僅供參考)
Kibana 界面如下圖所示:
但這些日志檢索原理是什么呢?這里就該我們的 Elasticsearch 搜索引擎登場了。
我會分為三篇來講解 Elasticsearch(簡稱ES)的原理、實戰及部署。
上篇:講解 ES 的原理、中文分詞的配置。中篇:實戰 ES 應用。下篇:ES 的集群部署。為什么要分成三篇,因為每一篇都很長,而且側重點不一樣,所以分成三篇來講解。
一、Elasticsearch 簡介
1.1 什么是 Elasticsearch?
Elasticsearch 是一個分布式的開源搜索和分析引擎,適用于所有類型的數據,包括文本、數字、地理空間、結構化和非結構化數據。簡單來說只要涉及搜索和分析相關的, ES 都可以做。
1.2 Elasticsearch 的用途?
Elasticsearch 在速度和可擴展性方面都表現出色,而且還能夠索引多種類型的內容,這意味著其可用于多種用例:
比如一個在線網上商店,您可以在其中允許客戶搜索您出售的產品。在這種情況下,您可以使用Elasticsearch 存儲整個產品目錄和庫存,并為它們提供搜索和自動完成建議。1.3 Elasticsearch 的工作原理?
Elasticsearch 是在 Lucene 基礎上構建而成的。ES 在 Lucence 上做了很多增強。
Lucene 是apache軟件基金會 4 的 jakarta 項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene的目的是為軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者是以此為基礎建立起完整的全文檢索引擎。(來自百度百科)
Elasticsearch 的原始數據從哪里來?
原始數據從多個來源 ( 包括日志、系統指標和網絡應用程序 ) 輸入到 Elasticsearch 中。
Elasticsearch 的數據是怎么采集的?
數據采集指在 Elasticsearch 中進行索引之前解析、標準化并充實這些原始數據的過程。這些數據在 Elasticsearch 中索引完成之后,用戶便可針對他們的數據運行復雜的查詢,并使用聚合來檢索自身數據的復雜匯總。這里用到了 Logstash,后面會介紹。
怎么可視化查看想要檢索的數據?
這里就要用到 Kibana 了,用戶可以基于自己的數據進行搜索、查看數據視圖等。
1.4 Elasticsearch 索引是什么?
Elasticsearch 索引指相互關聯的文檔集合。Elasticsearch 會以 JSON 文檔的形式存儲數據。每個文檔都會在一組鍵 ( 字段或屬性的名稱 ) 和它們對應的值 ( 字符串、數字、布爾值、日期、數值組、地理位置或其他類型的數據 ) 之間建立聯系。
Elasticsearch 使用的是一種名為倒排索引的數據結構,這一結構的設計可以允許十分快速地進行全文本搜索。倒排索引會列出在所有文檔中出現的每個特有詞匯,并且可以找到包含每個詞匯的全部文檔。
在索引過程中,Elasticsearch 會存儲文檔并構建倒排索引,這樣用戶便可以近實時地對文檔數據進行搜索。索引過程是在索引 API 中啟動的,通過此 API 您既可向特定索引中添加 JSON 文檔,也可更改特定索引中的 JSON 文檔。
1.5 Logstash 的用途是什么?
Logstash 就是 ELK
中的 L
。
Logstash 是 Elastic Stack 的核心產品之一,可用來對數據進行聚合和處理,并將數據發送到 Elasticsearch。Logstash 是一個開源的服務器端數據處理管道,允許您在將數據索引到 Elasticsearch 之前同時從多個來源采集數據,并對數據進行充實和轉換。
1.6 Kibana 的用途是什么?
Kibana 是一款適用于 Elasticsearch 的數據可視化和管理工具,可以提供實時的直方圖、線性圖等。
1.7 為什么使用 Elasticsearch
ES 很快,近實時的搜索平臺。ES 具有分布式的本質特質。ES 包含一系列廣泛的功能,比如數據匯總和索引生命周期管理。官方文檔:https://www.elastic.co/cn/what-is/elasticsearch
二、ES 基本概念
2.1 Index ( 索引 )
動詞:相當于 Mysql 中的 insert
名詞:相當于 Mysql 中的 database
與 mysql 的對比
序號 | Mysql | Elasticsearch |
---|---|---|
1 | Mysql 服務 | ES 集群服務 |
2 | 數據庫 Database | 索引 Index |
3 | 表 Table | 類型 Type |
4 | 記錄 Records ( 一行行記錄 ) | 文檔 Document ( JSON 格式 ) |
2.2 倒排索引
假如數據庫有如下電影記錄:
1-大話西游
2-大話西游外傳
3-解析大話西游
4-西游降魔外傳
5-夢幻西游獨家解析
分詞:將整句分拆為單詞
序號 | 保存到 ES 的詞 | 對應的電影記錄序號 |
---|---|---|
A | 西游 | 1,2, 3,4, 5 |
B | 大話 | 1,2, 3 |
C | 外傳 | 2,4, 5 |
D | 解析 | 3,5 |
E | 降魔 | 4 |
F | 夢幻 | 5 |
G | 獨家 | 5 |
檢索:獨家大話西游
將 獨家大話西游
解析拆分成 獨家
、大話
、西游
ES 中 A、B、G 記錄 都有這三個詞的其中一種, 所以 1,2, 3,4, 5 號記錄都有相關的詞被命中。
1 號記錄命中 2 次, A、B 中都有 ( 命中 2
次 ) ,而且 1 號記錄有 2
個詞,相關性得分:2
次/2
個詞=1
2 號記錄命中 2 個詞 A、B 中的都有 ( 命中 2
次 ) ,而且 2 號記錄有 2
個詞,相關性得分:2
次/3
個詞= 0.67
3 號記錄命中 2 個詞 A、B 中的都有 ( 命中 2
次 ) ,而且 3 號記錄有 2
個詞,相關性得分:2
次/3
個詞= 0.67
4 號記錄命中 2 個詞 A 中有 ( 命中 1
次 ) ,而且 4 號記錄有 3
個詞,相關性得分:1
次/3
個詞= 0.33
5 號記錄命中 2 個詞 A 中有 ( 命中 2
次 ) ,而且 4 號記錄有 4
個詞,相關性得分:2
次/4
個詞= 0.5
所以檢索出來的記錄順序如下:
1-大話西游 ( 想關性得分:1 )
2-大話西游外傳 ( 想關性得分:0.67 )
3-解析大話西游 ( 想關性得分:0.67 )
5-夢幻西游獨家解析 ( 想關性得分:0.5 )
4-西游降魔 ( 想關性得分:0.33 )
三、Docker 搭建環境
3.1. 搭建 Elasticsearch 環境
搭建虛擬機環境和安裝 docker 可以參照之前寫的文檔:
01. 快速搭建 Linux 環境-運維必備02. 配置虛擬機網絡03. 安裝 Docker1 ) 下載鏡像文件
docker pull elasticsearch:7.4.2
2 ) 創建實例
映射配置文件配置映射文件夾mkdir -p /mydata/elasticsearch/config配置映射文件夾mkdir -p /mydata/elasticsearch/data設置文件夾權限任何用戶可讀可寫chmod 777 /mydata/elasticsearch -R配置 http.hostecho "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml
啟動 elasticsearch 容器docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \-e "discovery.type"="single-node" \-e ES_JAVA_OPTS="-Xms64m -Xmx128m" \-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \-d elasticsearch:7.4.2
訪問 elasticsearch 服務訪問:http://192.168.56.10:9200
返回的 reponse
{ "name" : "8448ec5f3312", "cluster_name" : "elasticsearch", "cluster_uuid" : "xC72O3nKSjWavYZ-EPt9Gw", "version" : { "number" : "7.4.2", "build_flavor" : "default", "build_type" : "docker", "build_hash" : "2f90bbf7b93631e52bafb59b3b049cb44ec25e96", "build_date" : "2019-10-28T20:40:44.881551Z", "build_snapshot" : false, "lucene_version" : "8.2.0", "minimum_wire_compatibility_version" : "6.8.0", "minimum_index_compatibility_version" : "6.0.0-beta1" }, "tagline" : "You Know, for Search"}
訪問:http://192.168.56.10:9200/_cat 訪問節點信息
127.0.0.1 62 90 0 0.06 0.10 0.05 dilm * 8448ec5f3312
3.2. 搭建 Kibana 環境
docker pull kibana:7.4.2docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.56.10:9200 -p 5601:5601 -d kibana:7.4.2
訪問 kibana: http://192.168.56.10:5601/
四、初階檢索玩法
4.1._cat 用法
GET /_cat/nodes: 查看所有節點GET /_cat/health: 查看 es 健康狀況GET /_cat/master: 查看主節點GET /_cat/indices: 查看所有索引查詢匯總:/_cat/allocation/_cat/shards/_cat/shards/{index}/_cat/master/_cat/nodes/_cat/tasks/_cat/indices/_cat/indices/{index}/_cat/segments/_cat/segments/{index}/_cat/count/_cat/count/{index}/_cat/recovery/_cat/recovery/{index}/_cat/health/_cat/pending_tasks/_cat/aliases/_cat/aliases/{alias}/_cat/thread_pool/_cat/thread_pool/{thread_pools}/_cat/plugins/_cat/fielddata/_cat/fielddata/{fields}/_cat/nodeattrs/_cat/repositories/_cat/snapshots/{repository}/_cat/templates
4.2. 索引一個文檔 ( 保存 )
例子:在 customer
索引下的 external
類型下保存標識為 1
的數據。
PUT member/external/1{"name":"jay huang"}
Reponse:
{ "_index": "member", //在哪個索引 "_type": "external",//在那個類型 "_id": "2",//記錄 id "_version": 7,//版本號 "result": "updated",//操作類型 "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 9, "_primary_term": 1}
也可以通過 Postman 工具發送請求來創建記錄。注意:
PUT 和 POST 都可以創建記錄。
POST:如果不指定 id,自動生成 id。如果指定 id,則修改這條記錄,并新增版本號。
PUT:必須指定 id,如果沒有這條記錄,則新增,如果有,則更新。
4.3 查詢文檔
請求:http://192.168.56.10:9200/member/external/2Reposne:{ "_index": "member", //在哪個索引 "_type": "external", //在那個類型 "_id": "2", //記錄 id "_version": 7, //版本號 "_seq_no": 9, //并發控制字段,每次更新就會+1,用來做樂觀鎖 "_primary_term": 1, //同上,主分片重新分配,如重啟,就會變化 "found": true, "_source": { //真正的內容 "name": "jay huang" }}
_seq_no 用作樂觀鎖
每次更新完數據后,_seq_no 就會+1,所以可以用作并發控制。
當更新記錄時,如果_seq_no 與預設的值不一致,則表示記錄已經被至少更新了一次,不允許本次更新。
用法如下:
請求更新記錄 2: http://192.168.56.10:9200/member/external/2?if_seq_no=9&&if_primary_term=1返回結果:{ "_index": "member", "_type": "external", "_id": "2", "_version": 9, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 11, "_primary_term": 1}
_seq_no 等于 10,且_primary_term=1 時更新數據,執行一次請求后,再執行上面的請求則會報錯:版本沖突
{ "error": { "root_cause": [ { "type": "version_conflict_engine_exception", "reason": "[2]: version conflict, required seqNo [10], primary term [1]. current document has seqNo [11] and primary term [1]", "index_uuid": "CX6uwPBKRByWpuym9rMuxQ", "shard": "0", "index": "member" } ], "type": "version_conflict_engine_exception", "reason": "[2]: version conflict, required seqNo [10], primary term [1]. current document has seqNo [11] and primary term [1]", "index_uuid": "CX6uwPBKRByWpuym9rMuxQ", "shard": "0", "index": "member" }, "status": 409}
4.4 更新文檔
用法POST 帶 _update
的更新操作,如果原數據沒有變化,則 repsonse 中的 result 返回 noop ( 沒有任何操作 ) ,version 也不會變化。
請求體中需要用 doc
將請求數據包裝起來。
POST 請求:http://192.168.56.10:9200/member/external/2/_update{ "doc":{ "name":"jay huang" }}響應:{ "_index": "member", "_type": "external", "_id": "2", "_version": 12, "result": "noop", "_shards": { "total": 0, "successful": 0, "failed": 0 }, "_seq_no": 14, "_primary_term": 1}
使用場景:對于大并發更新,建議不帶 _update
。對于大并發查詢,少量更新的場景,可以帶_update,進行對比更新。
請求體中增加 age
屬性
http://192.168.56.10:9200/member/external/2/_updaterequest:{ "doc":{ "name":"jay huang", "age": 18 }}response:{ "_index": "member", "_type": "external", "_id": "2", "_version": 13, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 15, "_primary_term": 1}
4.5 刪除文檔和索引
刪除文檔DELETE 請求:http://192.168.56.10:9200/member/external/2response:{ "_index": "member", "_type": "external", "_id": "2", "_version": 2, "result": "deleted", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 1, "_primary_term": 1}
刪除索引DELETE 請求:http://192.168.56.10:9200/memberrepsonse:{ "acknowledged": true}
沒有刪除類型的功能4.6 批量導入數據
使用 kinaba 的 dev tools 工具,輸入以下語句
POST /member/external/_bulk{"index":{"_id":"1"}}{"name":"Jay Huang"}{"index":{"_id":"2"}}{"name":"Jackson Huang"}
執行結果如下圖所示:
https://raw.githubusercontent.com/elastic/elasticsearch/master/docs/src/test/resources/accounts.json
POST /bank/account/_bulk{"index":{"_id":"1"}}{"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}{"index":{"_id":"6"}}......
可以從返回結果中看到 bank 索引有 1000 條數據,占用了 440.2kb 存儲空間。
五、高階檢索玩法
5.1 兩種查詢方式
5.1.1 URL 后接參數
GET bank/_search?q=*&sort=account_number: asc
took – ES 執行搜索的時間 ( 毫秒 )
timed_out – ES 是否超時
_shards – 有多少個分片被搜索了,以及統計了成功/失敗/跳過的搜索的分片
max_score – 最高得分
hits.total.value - 命中多少條記錄
hits.sort - 結果的排序 key 鍵,沒有則按 score 排序
hits._score - 相關性得分
參考文檔:
https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-search.html
### 5.1.2 URL 加請求體進行檢索 ( QueryDSL )請求體中寫查詢條件語法:```json
GET bank/_search{ "query":{"match_all": {}}, "sort": [{"account_number": "asc" } ]}
示例:查詢出所有,先按照 accout_number 升序排序,再按照 balance 降序排序## 5.2 詳解 QueryDSL 查詢> DSL: Domain Specific Language### 5.2.1 全部匹配 match_all示例:查詢所有記錄,按照 balance 降序排序,只返回第 11 條記錄到第 20 條記錄,只顯示 balance 和 firstname 字段。```json
GET bank/_search{ "query": {"match_all": {} }, "sort": [ { "balance": { "order": "desc" } } ], "from": 10, "size": 10, "_source": "balance", "firstname"}
### 5.2.2 匹配查詢 match- 基本類型 ( 非字符串 ) ,精確匹配```json
GET bank/_search{ "query": {"match": {"account_number": "30"} }}- 字符串,全文檢索```jsonGET bank/_search{ "query": {"match": { "address": "mill road" } }}
> 全文檢索按照評分進行排序,會對檢索條件進行分詞匹配。>> 查詢 `address` 中包含 `mill` 或者 `road` 或者 `mill road` 的所有記錄,并給出相關性得分。查到了 32 條記錄,最高的一條記錄是 Address = "990 Mill Road",得分:8.926605. Address="198 Mill Lane" 評分 5.4032025,只匹配到了 Mill 單詞。### 5.2.3 短語匹配 match_phase將需要匹配的值當成一個整體單詞 ( 不分詞 ) 進行檢索```json
GET bank/_search{ "query": {"match_phrase": { "address": "mill road" } }}> 查出 address 中包含 `mill road` 的所有記錄,并給出相關性得分### 5.2.4 多字段匹配 multi_match```jsonGET bank/_search{ "query": {
"multi_match": { "query": "mill land", "fields": [ "state", "address" ] } }}
> multi_match 中的 query 也會進行分詞。>> 查詢 `state` 包含 `mill` 或 `land` 或者 `address` 包含 `mill` 或 `land` 的記錄。### 5.2.5 復合查詢 bool> 復合語句可以合并任何其他查詢語句,包括復合語句。復合語句之間可以相互嵌套,可以表達復雜的邏輯。搭配使用 must,must_not,shouldmust: 必須達到 must 指定的條件。 ( 影響相關性得分 )must_not: 必須不滿足 must_not 的條件。 ( 不影響相關性得分 )should: 如果滿足 should 條件,則可以提高得分。如果不滿足,也可以查詢出記錄。 ( 影響相關性得分 )示例:查詢出地址包含 mill,且性別為 M,年齡不等于 28 的記錄,且優先展示 firstname 包含 Winnie 的記錄。```json
GET bank/_search{ "query": {"bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "gender": "M" } } ], "must_not": [ { "match": { "age": "28" } } ], "should": [ { "match": { "firstname": "Winnie" } } ] } }}
### 5.2.6 filter 過濾> 不影響相關性得分,查詢出滿足 filter 條件的記錄。>> 在 bool 中使用。```json
GET bank/_search{ "query": {"bool": { "filter": [ { "range": { "age": { "gte":18, "lte":40 } ] }
### 5.2.7 term 查詢> 匹配某個屬性的值。>> 全文檢索字段用 match,其他非 text 字段匹配用 term>> keyword:文本精確匹配 ( 全部匹配 )>> match_phase:文本短語匹配```json非 text 字段精確匹配GET bank/_search{ "query": {"term": { "age": "20" }
### 5.2.8 aggregations 聚合> 聚合:從數據中分組和提取數據。類似于 SQL GROUP BY 和 SQL 聚合函數。>> Elasticsearch 可以將命中結果和多個聚合結果同時返回。聚合語法:```json"aggregations" : {"<聚合名稱 1>" : { "<聚合類型>" : { <聚合體內容> } [,"元數據" : { [] }]? [,"aggregations" : { []+ }]?}[,"聚合名稱 2>" : { ... }]*}- 示例 1:搜索 address 中包含 big 的所有人的年齡分布 ( 前 10 條 ) 以及平均年齡,以及平均薪資```jsonGET bank/_search{ "query": {"match": { "address": "mill" } }, "aggs": {"ageAggr": { "terms": { "field": "age", "size": 10 }},"ageAvg": { "avg": { "field": "age" }},"balanceAvg": { "avg": { "field": "balance" } } }}
檢索結果如下所示:hits 記錄返回了,三種聚合結果也返回了,平均年齡 34 隨,平均薪資 25208.0,品駿年齡分布:38 歲的有 2 個,28 歲的有一個,32 歲的有一個如果不想返回 hits 結果,可以在最后面設置 size:0```jsonGET bank/_search{ "query": {"match": { "address": "mill" } }, "aggs": {"ageAggr": { "terms": { "field": "age", "size": 10 }} }, "size": 0}- 示例 2:按照年齡聚合,并且查詢這些年齡段的平均薪資從結果可以看到 31 歲的有 61 個,平均薪資 28312.9,其他年齡的聚合結果類似。- 示例 3:按照年齡分組,然后將分組后的結果按照性別分組,然后查詢出這些分組后的平均薪資```jsonGET bank/_search{ "query": {"match_all": { } }, "aggs": {"ageAggr": { "terms": { "field": "age", "size": 10 }, "aggs": { "genderAggr": { "terms": { "field": "gender.keyword", "size": 10 }, "aggs": { "balanceAvg": { "avg": { "field": "balance" } } }} } } }, "size": 0}
從結果可以看到 31 歲的有 61 個。其中性別為 `M` 的 35 個,平均薪資 29565.6,性別為 `F` 的 26 個,平均薪資 26626.6。其他年齡的聚合結果類似。### 5.2.9 Mapping 映射> Mapping 是用來定義一個文檔 ( document ) ,以及它所包含的屬性 ( field ) 是如何存儲和索引的。- 定義哪些字符串屬性應該被看做全文本屬性 ( full text fields )- 定義哪些屬性包含數字,日期或地理位置- 定義文檔中的所有屬性是否都能被索引 ( _all 配置 )- 日期的格式- 自定義映射規則來執行動態添加屬性Elasticsearch7 去掉 tpye 概念:關系型數據庫中兩個數據庫表示是獨立的,即使他們里面有相同名稱的列也不影響使用,但 ES 中不是這樣的。elasticsearch 是基于 Lucence 開發的搜索引擎,而 ES 中不同 type 下名稱相同的 field 最終在 Lucence 中的處理方式是一樣的。為了區分不同 type 下的同一名稱的字段,Lucence 需要處理沖突,導致檢索效率下降ES7.x 版本:URL 中的 type 參數為可選。ES8.x 版本:不支持 URL 中的 type 參數所有類型可以參考文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html- 查詢索引的映射如查詢 my-index 索引的映射```json
GET /my-index/_mapping返回結果:{ "my-index" : {"mappings" : { "properties" : { "age" : { "type" : "integer" }, "email" : { "type" : "keyword" }, "employee-id" : { "type" : "keyword", "index" : false }, "name" : { "type" : "text" }} }
- 創建索引并指定映射如創建 my-index 索引,有三個字段 age,email,name,指定類型為 interge, keyword, text```jsonPUT /my-index{ "mappings": {"properties": { "age": { "type": "integer" }, "email": { "type": "keyword" }, "name": { "type": "text" }}
返回結果:
{ "acknowledged" : true, "shards_acknowledged" : true, "index" : "my-index"}
- 添加新的字段映射如在 my-index 索引里面添加 employ-id 字段,指定類型為 keyword```jsonPUT /my-index/_mapping{ "properties": {"employee-id": { "type": "keyword", "index": false} }}
- 更新映射> 我們不能更新已經存在的映射字段,必須創建新的索引進行數據遷移。- 數據遷移```jsonPOST _reindex{ "source": {"index": "twitter" }, "dest": {"index": "new_twitter" }}
# 六、中文分詞ES 內置了很多種分詞器,但是對中文分詞不友好,所以我們需要借助第三方中文分詞工具包。## 6.1 ES 中的分詞的原理### 6.1.1 ES 的分詞器概念ES 的一個分詞器 ( tokenizer ) 接收一個字符流,將其分割為獨立的詞元 ( tokens ) ,然后輸出詞元流。ES 提供了很多內置的分詞器,可以用來構建自定義分詞器 ( custom ananlyzers )### 6.1.2 標準分詞器原理比如 stadard tokenizer 標準分詞器,遇到空格進行分詞。該分詞器還負責記錄各個詞條 ( term ) 的順序或 position 位置 ( 用于 phrase 短語和 word proximity 詞近鄰查詢 ) 。每個單詞的字符偏移量 ( 用于高亮顯示搜索的內容 ) 。### 6.1.3 英文和標點符號分詞示例查詢示例如下:```jsonPOST _analyze{ "analyzer": "standard", "text": "Do you know why I want to study ELK? 2 3 33..."}
查詢結果:```shdo, you, know, why, i, want, to, study, elk, 2,3,33
從查詢結果可以看到:(1)標點符號沒有分詞。(2)數字會進行分詞。### 6.1.4 中文分詞示例但是這種分詞器對中文的分詞支持不友好,會將詞語分詞為單獨的漢字。比如下面的示例會將 ` 悟空聊架構 ` 分詞為 ` 悟 `,` 空 `,` 聊 `,` 架 `,` 構 `,期望分詞為 ` 悟空 `,` 聊 `,` 架構 `。```json
POST _analyze{ "analyzer": "standard", "text": "悟空聊架構"}
我們可以安裝 ik 分詞器來更加友好的支持中文分詞。## 6.2 安裝 ik 分詞器### 6.2.1 ik 分詞器地址ik 分詞器地址:```js
https://github.com/medcl/elasticsearch-analysis-ik/releases
先檢查 ES 版本,我安裝的版本是 `7.4.2`,所以我們安裝 ik 分詞器的版本也選擇 7.4.2```json
http://192.168.56.10:9200/{ "name" : "8448ec5f3312", "cluster_name" : "elasticsearch", "cluster_uuid" : "xC72O3nKSjWavYZ-EPt9Gw", "version" : {"number" : "7.4.2","build_flavor" : "default","build_type" : "docker","build_hash" : "2f90bbf7b93631e52bafb59b3b049cb44ec25e96","build_date" : "2019-10-28T20:40:44.881551Z","build_snapshot" : false,"lucene_version" : "8.2.0","minimum_wire_compatibility_version" : "6.8.0","minimum_index_compatibility_version" : "6.0.0-beta1" }, "tagline" : "You Know, for Search"}
### 6.2.2 安裝 ik 分詞器的方式#### 6.2.2.1 方式一:容器內安裝 ik 分詞器- 進入 es 容器內部 plugins 目錄```sh
docker exec -it <容器 id> /bin/bash
- 獲取 ik 分詞器壓縮包```sh
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
- 解壓縮 ik 壓縮包```sh
unzip 壓縮包
- 刪除下載的壓縮包```sh
rm -rf *.zip
#### 6.2.2.2 方式二:映射文件安裝 ik 分詞器進入到映射文件夾```sh
cd /mydata/elasticsearch/plugins
下載安裝包```sh
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
- 解壓縮 ik 壓縮包```sh
unzip 壓縮包
- 刪除下載的壓縮包```sh
rm -rf *.zip
#### 6.2.2.3 方式三:Xftp 上傳壓縮包到映射目錄先用 XShell 工具連接虛擬機 ( 操作步驟可以參考之前寫的文章 [02. 快速搭建 Linux 環境-運維必備](http://www.passjava.cn/#/01.PassJava/03.Deploy/01.環境搭建篇)) ,然后用 Xftp 將下載好的安裝包復制到虛擬機。## 6.3 解壓 ik 分詞器到容器中- 如果沒有安裝 unzip 解壓工具,則安裝 unzip 解壓工具。```sh
apt install unzip
- 解壓 ik 分詞器到當前目錄的 ik 文件夾下。命令格式:unzip 實例:```sh
unzip ELK-IKv7.4.2.zip -d ./ik
- 修改文件夾權限為可讀可寫。```sh
chmod -R 777 ik/
- 刪除 ik 分詞器壓縮包```sh
rm ELK-IKv7.4.2.zip
## 6.4 檢查 ik 分詞器安裝- 進入到容器中```sh
docker exec -it <容器 id> /bin/bash
- 查看 Elasticsearch 的插件```sh
elasticsearch-plugin list
結果如下,說明 ik 分詞器安裝好了。是不是很簡單。```sh
ik
然后退出 Elasticsearch 容器,并重啟 Elasticsearch 容器```sh
exit
docker restart elasticsearch
## 6.5 使用 ik 中文分詞器ik 分詞器有兩種模式- 智能分詞模式 ( ik_smart )- 最大組合分詞模式 ( ik_max_word )我們先看下 ` 智能分詞 ` 模式的效果。比如對于 ` 一顆小星星 ` 進行中文分詞,得到的兩個詞語:` 一顆 `、` 小星星 `我們在 Dev Tools Console 輸入如下查詢```json
POST _analyze{ "analyzer": "ik_smart", "text": "一顆小星星"}
得到如下結果,被分詞為 一顆和小星星。再來看下 ` 最大組合分詞模式 `。輸入如下查詢語句。```json
POST _analyze{ "analyzer": "ik_max_word", "text": "一顆小星星"}
` 一顆小星星 ` 被分成了 6 個詞語:一顆、一、顆、小星星、小星、星星。我們再來看下另外一個中文分詞。比如搜索悟空哥聊架構,期望結果:悟空哥、聊、架構三個詞語。實際結果:悟、空哥、聊、架構四個詞語。ik 分詞器將悟空哥分詞了,認為 ` 空哥 ` 是一個詞語。所以需要讓 ik 分詞器知道 ` 悟空哥 ` 是一個詞語,不需要拆分。那怎么辦做呢? ## 6.6 自定義分詞詞庫### 6.6.1 自定義詞庫的方案- 方案 新建一個詞庫文件,然后在 ik 分詞器的配置文件中指定分詞詞庫文件的路徑。可以指定本地路徑,也可以指定遠程服務器文件路徑。這里我們使用遠程服務器文件的方案,因為這種方案可以支持熱更新 ( 更新服務器文件,ik 分詞詞庫也會重新加載 ) 。- 修改配置文件ik 分詞器的配置文件在容器中的路徑:```sh
/usr/share/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml。修改這個文件可以通過修改映射文件,文件路徑:```sh/mydata/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml編輯配置文件:```shvim /mydata/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml配置文件內容如下所示:```xml
IK Analyzer 擴展配置
custom/mydict.dic;custom/single_word_low_freq.dic
custom/ext_stopword.dic
location
http://xxx.com/xxx.dic
修改配置 `remote_ext_dict` 的屬性值,指定一個 遠程網站文件的路徑,比如 http://www.xxx.com/ikwords.text。這里我們可以自己搭建一套 nginx 環境,然后把 ikwords.text 放到 nginx 根目錄。### 6.6.2 搭建 nginx 環境方案:首先獲取 nginx 鏡像,然后啟動一個 nginx 容器,然后將 nginx 的配置文件拷貝到根目錄,再刪除原 nginx 容器,再用映射文件夾的方式來重新啟動 nginx 容器。- 通過 docker 容器安裝 nginx 環境。```sh
docker run -p 80:80 --name nginx -d nginx:1.10
- 拷貝 nginx 容器的配置文件到 mydata 目錄的 conf 文件夾```sh
cd /mydata
docker container cp nginx:/etc/nginx ./conf
- mydata 目錄 里面創建 nginx 目錄```sh
mkdir nginx
- 移動 conf 文件夾到 nginx 映射文件夾```sh
mv conf nginx/
- 終止并刪除原 nginx 容器```sh
docker stop nginx
docker rm <容器 id>
- 啟動新的容器```sh
docker run -p 80:80 --name nginx \
-v /mydata/nginx/html:/usr/share/nginx/html \
-v /mydata/nginx/logs:/var/log/nginx \
-v /mydata/nginx/conf:/etc/nginx \
-d nginx:1.10
- 訪問 nginx 服務```sh
192.168.56.10
報 403 Forbidden, nginx/1.10.3 則表示 nginx 服務正常啟動。403 異常的原因是 nginx 服務下沒有文件。- nginx 目錄新建一個 html 文件```sh
cd /mydata/nginx/html
vim index.html
hello passjava
- 再次訪問 nginx 服務 瀏覽器打印 hello passjava。說明訪問 nginx 服務的頁面沒有問題。- 創建 ik 分詞詞庫文件```sh
cd /mydata/nginx/html
mkdir ik
cd ik
vim ik.txt
填寫 ` 悟空哥 `,并保存文件。- 訪問詞庫文件```sh
http://192.168.56.10/ik/ik.txt
瀏覽器會輸出一串亂碼,可以先忽略亂碼問題。說明詞庫文件可以訪問到。- 修改 ik 分詞器配置```sh
cd /mydata/elasticsearch/plugins/ik/config
vim IKAnalyzer.cfg.xml
- 重啟 elasticsearch 容器并設置每次重啟機器后都啟動 elasticsearch 容器。```sh
docker restart elasticsearch
docker update elasticsearch --restart=always
再次查詢分詞結果可以看到 悟空哥聊架構
被拆分為 悟空哥
、聊
、架構
三個詞語,說明自定義詞庫中的 悟空哥
有作用。
七、寫在最后
中篇和下篇繼續肝,加油沖呀!
中篇: 實戰 ES 應用。下篇: ES 的集群部署。我是悟空哥,努力變強,變身超級賽亞人!我們下期見!