別隻會搜日誌了,求你懂點檢索原理吧

本篇主要內容以下:javascript

主要內容

前言

項目中咱們老是用 Kibana 界面來搜索測試或生產環境下的日誌,來看下有沒有異常信息。Kibana 就是 咱們常說的 ELK 中的 Khtml

​Kibana 界面以下圖所示:java

Kibana 界面

但這些日誌檢索原理是什麼呢?這裏就該咱們的 Elasticsearch 搜索引擎登場了。node

我會分爲三篇來說解 Elasticsearch 的原理、實戰及部署。 本篇是講解 ES 的上篇。mysql

  • 上篇: 講解 Elasticsearch(簡稱ES)的原理、中文分詞的配置。
  • 中篇: 實戰 ES 應用。
  • 下篇: ES 的集羣部署。

爲何要分紅三篇,由於每一篇都很長,並且側重點不同,因此分紅三篇來說解。nginx

1、Elasticsearch 簡介

1.1 什麼是 Elasticsearch?

Elasticsearch 是一個分佈式的開源搜索和分析引擎,適用於全部類型的數據,包括文本、數字、地理空間、結構化和非結構化數據。簡單來講只要涉及搜索和分析相關的, ES 均可以作。git

1.2 Elasticsearch 的用途?

Elasticsearch 在速度和可擴展性方面都表現出色,並且還可以索引多種類型的內容,這意味着其可用於多種用例:github

  • 好比一個在線網上商店,您能夠在其中容許客戶搜索您出售的產品。在這種狀況下,您可使用Elasticsearch 存儲整個產品目錄和庫存,併爲它們提供搜索和自動完成建議。

搜索手機

  • 好比收集日誌或交易數據,而且要分析和挖掘此數據以查找趨勢,統計信息,摘要或異常。在這種狀況下,您可使用 Logstash(Elasticsearch / Logstash / Kibana堆棧的一部分)來收集,聚合和解析數據,而後讓 Logstash 將這些數據提供給 Elasticsearch。數據放入 Elasticsearch 後,您能夠運行搜索和聚合以挖掘您感興趣的任何信息。

1.3 Elasticsearch 的工做原理?

ELK 原理圖

Elasticsearch 是在 Lucene 基礎上構建而成的。ES 在 Lucence 上作了不少加強。面試

Lucene 是apache軟件基金會 4 的 jakarta 項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene的目的是爲軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者是以此爲基礎創建起完整的全文檢索引擎。(來自百度百科)sql

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

2、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 號記錄有 2 個詞,相關性得分: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 )

3、Docker 搭建環境

3.1. 搭建 Elasticsearch 環境

搭建虛擬機環境和安裝 docker 能夠參照以前寫的文檔:

1 ) 下載鏡像文件

docker pull elasticsearch:7.4.2

2 ) 建立實例

    1. 映射配置文件
配置映射文件夾
mkdir -p /mydata/elasticsearch/config

配置映射文件夾
mkdir -p /mydata/elasticsearch/data

設置文件夾權限任何用戶可讀可寫
chmod 777 /mydata/elasticsearch -R

配置 http.host
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml
    1. 啓動 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
    1. 訪問 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.2

docker 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/

kibana

4、初階檢索玩法

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 的數據。

  • 使用 Kibana 的 Dev Tools 來建立
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/2

Reposne:
{
    "_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/_update
request:
{
    "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/2
response:
{
    "_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/member
repsonse:
{
    "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

    官方樣本數據

  • 在 kibana 中執行腳本
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、高階檢索玩法

5.1 兩種查詢方式

5.1.1 URL 後接參數

GET bank/_search?q=*&sort=account_number: asc

``/_search?q=*&sort=account_number: asc

查詢出全部數據,共 1000 條數據,耗時 1ms,只展現 10 條數據 ( ES 分頁 )

URL 後接參數

屬性值說明:

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 )

請求體中寫查詢條件

語法:

GET bank/_search
{
  "query":{"match_all": {}},
  "sort": [
    {"account_number": "asc" }
 ]
}

示例:查詢出全部,先按照 accout_number 升序排序,再按照 balance 降序排序

URL 加請求體進行檢索

5.2 詳解 QueryDSL 查詢

DSL: Domain Specific Language

5.2.1 所有匹配 match_all

示例:查詢全部記錄,按照 balance 降序排序,只返回第 11 條記錄到第 20 條記錄,只顯示 balance 和 firstname 字段。

GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
 {
      "balance": {
        "order": "desc"
 }
 }
  ],
  "from": 10,
  "size": 10,
  "_source": ["balance", "firstname"]
}

5.2.2 匹配查詢 match

  • 基本類型 ( 非字符串 ) ,精確匹配
GET bank/_search
{
  "query": {
    "match": {"account_number": "30"}
 }
}
  • 字符串,全文檢索
GET 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

將須要匹配的值當成一個總體單詞 ( 不分詞 ) 進行檢索

GET bank/_search
{
  "query": {
    "match_phrase": {
      "address": "mill road"
 }
 }
}

查出 address 中包含 mill road 的全部記錄,並給出相關性得分

5.2.4 多字段匹配 multi_match

GET bank/_search
{
  "query": {
    "multi_match": {
      "query": "mill land",
      "fields": [
        "state",
        "address"
 ]
 }
 }
}

multi_match 中的 query 也會進行分詞。

查詢 state 包含 millland 或者 address 包含 millland 的記錄。

5.2.5 複合查詢 bool

複合語句能夠合併任何其餘查詢語句,包括複合語句。複合語句之間能夠相互嵌套,能夠表達複雜的邏輯。

搭配使用 must,must_not,should

must: 必須達到 must 指定的條件。 ( 影響相關性得分 )

must_not: 必須不知足 must_not 的條件。 ( 不影響相關性得分 )

should: 若是知足 should 條件,則能夠提升得分。若是不知足,也能夠查詢出記錄。 ( 影響相關性得分 )

示例:查詢出地址包含 mill,且性別爲 M,年齡不等於 28 的記錄,且優先展現 firstname 包含 Winnie 的記錄。

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 中使用。

GET bank/_search
{
  "query": {
    "bool": {
      "filter": [
 {
          "range": {
            "age": {
              "gte":18,
              "lte":40
 }
 }
 }
 ]
 }
 }
}

5.2.7 term 查詢

匹配某個屬性的值。

全文檢索字段用 match,其餘非 text 字段匹配用 term

keyword:文本精確匹配 ( 所有匹配 )

match_phase:文本短語匹配

非 text 字段精確匹配
GET bank/_search
{
  "query": {
    "term": {
      "age": "20"
 }
 }
}

5.2.8 aggregations 聚合

聚合:從數據中分組和提取數據。相似於 SQL GROUP BY 和 SQL 聚合函數。

Elasticsearch 能夠將命中結果和多個聚合結果同時返回。

聚合語法:

"aggregations" : {
    "<聚合名稱 1>" : {
        "<聚合類型>" : {
            <聚合體內容>
        }
        [,"元數據" : {  [<meta_data_body>] }]?
        [,"aggregations" : { [<sub_aggregation>]+ }]?
    }
    [,"聚合名稱 2>" : { ... }]*
}
  • 示例 1:搜索 address 中包含 big 的全部人的年齡分佈 ( 前 10 條 ) 以及平均年齡,以及平均薪資
GET 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 歲的有一個

示例 1

若是不想返回 hits 結果,能夠在最後面設置 size:0

GET bank/_search
{
  "query": {
    "match": {
      "address": "mill"
 }
  },
  "aggs": {
    "ageAggr": {
      "terms": {
        "field": "age",
        "size": 10
      }
    }
  },
  "size": 0
}
  • 示例 2:按照年齡聚合,而且查詢這些年齡段的平均薪資

從結果能夠看到 31 歲的有 61 個,平均薪資 28312.9,其餘年齡的聚合結果相似。

示例 2

  • 示例 3:按照年齡分組,而後將分組後的結果按照性別分組,而後查詢出這些分組後的平均薪資
GET 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 索引的映射

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

PUT /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

PUT /my-index/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
 }
}
  • 更新映射

咱們不能更新已經存在的映射字段,必須建立新的索引進行數據遷移。

  • 數據遷移
POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
 }
}

6、中文分詞

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 英文和標點符號分詞示例

查詢示例以下:

POST _analyze
{
  "analyzer": "standard",
  "text": "Do you know why I want to study ELK? 2 3 33..."
}

查詢結果:

do, you, know, why, i, want, to, study, elk, 2,3,33

從查詢結果能夠看到:

(1)標點符號沒有分詞。

(2)數字會進行分詞。

英文句子分詞

6.1.4 中文分詞示例

可是這種分詞器對中文的分詞支持不友好,會將詞語分詞爲單獨的漢字。好比下面的示例會將 悟空聊架構 分詞爲 ,,,,,指望分詞爲 悟空架構

POST _analyze
{
  "analyzer": "standard",
  "text": "悟空聊架構"
}

中文分詞悟空聊架構

咱們能夠安裝 ik 分詞器來更加友好的支持中文分詞。

6.2 安裝 ik 分詞器

6.2.1 ik 分詞器地址

ik 分詞器地址:

https://github.com/medcl/elasticsearch-analysis-ik/releases

先檢查 ES 版本,我安裝的版本是 7.4.2,因此咱們安裝 ik 分詞器的版本也選擇 7.4.2

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"
}

選擇 ik 分詞器

6.2.2 安裝 ik 分詞器的方式

6.2.2.1 方式一:容器內安裝 ik 分詞器

  • 進入 es 容器內部 plugins 目錄
docker exec -it <容器 id> /bin/bash
  • 獲取 ik 分詞器壓縮包
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
  • 解壓縮 ik 壓縮包
unzip 壓縮包
  • 刪除下載的壓縮包
rm -rf *.zip

6.2.2.2 方式二:映射文件安裝 ik 分詞器

進入到映射文件夾

cd /mydata/elasticsearch/plugins

下載安裝包

wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
  • 解壓縮 ik 壓縮包
unzip 壓縮包
  • 刪除下載的壓縮包
rm -rf *.zip

6.2.2.3 方式三:Xftp 上傳壓縮包到映射目錄

先用 XShell 工具鏈接虛擬機 ( 操做步驟能夠參考以前寫的文章 [02. 快速搭建 Linux 環境-運維必備] ( http://www.jayh.club/#/05. 安裝部署篇/01. 環境搭建篇 )) ,而後用 Xftp 將下載好的安裝包複製到虛擬機。

Xftp 上傳壓縮包

6.3 解壓 ik 分詞器到容器中

  • 若是沒有安裝 unzip 解壓工具,則安裝 unzip 解壓工具。
apt install unzip
  • 解壓 ik 分詞器到當前目錄的 ik 文件夾下。

命令格式:unzip <ik 分詞器壓縮包>

實例:

unzip ELK-IKv7.4.2.zip -d ./ik

解壓 ik 分詞器

  • 修改文件夾權限爲可讀可寫。
chmod -R 777 ik/
  • 刪除 ik 分詞器壓縮包
rm ELK-IKv7.4.2.zip

6.4 檢查 ik 分詞器安裝

  • 進入到容器中
docker exec -it <容器 id> /bin/bash
  • 查看 Elasticsearch 的插件
elasticsearch-plugin list

結果以下,說明 ik 分詞器安裝好了。是否是很簡單。

ik

ik 分詞器插件

而後退出 Elasticsearch 容器,並重啓 Elasticsearch 容器

exit
docker restart elasticsearch

6.5 使用 ik 中文分詞器

ik 分詞器有兩種模式

  • 智能分詞模式 ( ik_smart )

  • 最大組合分詞模式 ( ik_max_word )

咱們先看下 智能分詞 模式的效果。好比對於 一顆小星星 進行中文分詞,獲得的兩個詞語:一顆小星星

咱們在 Dev Tools Console 輸入以下查詢

POST _analyze
{
  "analyzer": "ik_smart",
  "text": "一顆小星星"
}

獲得以下結果,被分詞爲 一顆和小星星。

一顆小星星分詞結果

再來看下 最大組合分詞模式。輸入以下查詢語句。

POST _analyze
{
  "analyzer": "ik_max_word",
  "text": "一顆小星星"
}

一顆小星星 被分紅了 6 個詞語:一顆、1、顆、小星星、小星、星星。

一顆小星星分詞結果

咱們再來看下另一箇中文分詞。好比搜索悟空哥聊架構,指望結果:悟空哥、聊、架構三個詞語。

實際結果:悟、空哥、聊、架構四個詞語。ik 分詞器將悟空哥分詞了,認爲 空哥 是一個詞語。因此須要讓 ik 分詞器知道 悟空哥 是一個詞語,不須要拆分。那怎麼辦作呢?

悟空哥聊架構分詞

6.5 自定義分詞詞庫

6.5.1 自定義詞庫的方案

  • 方案

    新建一個詞庫文件,而後在 ik 分詞器的配置文件中指定分詞詞庫文件的路徑。能夠指定本地路徑,也能夠指定遠程服務器文件路徑。這裏咱們使用遠程服務器文件的方案,由於這種方案能夠支持熱更新 ( 更新服務器文件,ik 分詞詞庫也會從新加載 ) 。

  • 修改配置文件

ik 分詞器的配置文件在容器中的路徑:

/usr/share/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml。

修改這個文件能夠經過修改映射文件,文件路徑:

/mydata/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml

編輯配置文件:

vim /mydata/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml

配置文件內容以下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IK Analyzer 擴展配置</comment>
    <!--用戶能夠在這裏配置本身的擴展字典 -->
    <entry key="ext_dict">custom/mydict.dic;custom/single_word_low_freq.dic</entry>
     <!--用戶能夠在這裏配置本身的擴展中止詞字典-->
    <entry key="ext_stopwords">custom/ext_stopword.dic</entry>
     <!--用戶能夠在這裏配置遠程擴展字典 -->
    <entry key="remote_ext_dict">location</entry>
     <!--用戶能夠在這裏配置遠程擴展中止詞字典-->
    <entry key="remote_ext_stopwords">http://xxx.com/xxx.dic</entry>
</properties>

修改配置 remote_ext_dict 的屬性值,指定一個 遠程網站文件的路徑,好比 http://www.xxx.com/ikwords.text

這裏咱們能夠本身搭建一套 nginx 環境,而後把 ikwords.text 放到 nginx 根目錄。

6.5.2 搭建 nginx 環境

方案:首先獲取 nginx 鏡像,而後啓動一個 nginx 容器,而後將 nginx 的配置文件拷貝到根目錄,再刪除原 nginx 容器,再用映射文件夾的方式來從新啓動 nginx 容器。

  • 經過 docker 容器安裝 nginx 環境。
docker run -p 80:80 --name nginx -d nginx:1.10
  • 拷貝 nginx 容器的配置文件到 mydata 目錄的 conf 文件夾
cd /mydata
docker container cp nginx:/etc/nginx ./conf
  • mydata 目錄 裏面建立 nginx 目錄
mkdir nginx
  • 移動 conf 文件夾到 nginx 映射文件夾
mv conf nginx/
  • 終止並刪除原 nginx 容器
docker stop nginx
docker rm <容器 id>
  • 啓動新的容器
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 服務
192.168.56.10

報 403 Forbidden, nginx/1.10.3 則表示 nginx 服務正常啓動。403 異常的緣由是 nginx 服務下沒有文件。

  • nginx 目錄新建一個 html 文件
cd /mydata/nginx/html
vim index.html
hello passjava
  • 再次訪問 nginx 服務

    瀏覽器打印 hello passjava。說明訪問 nginx 服務的頁面沒有問題。

  • 建立 ik 分詞詞庫文件
cd /mydata/nginx/html
mkdir ik
cd ik
vim ik.txt

填寫 悟空哥,並保存文件。

  • 訪問詞庫文件
http://192.168.56.10/ik/ik.txt

瀏覽器會輸出一串亂碼,能夠先忽略亂碼問題。說明詞庫文件能夠訪問到。

  • 修改 ik 分詞器配置
cd /mydata/elasticsearch/plugins/ik/config
vim IKAnalyzer.cfg.xml

修改 ik 分詞器配置

  • 重啓 elasticsearch 容器並設置每次重啓機器後都啓動 elasticsearch 容器。
docker restart elasticsearch
docker update elasticsearch --restart=always
  • 再次查詢分詞結果

能夠看到 悟空哥聊架構 被拆分爲 悟空哥架構 三個詞語,說明自定義詞庫中的 悟空哥 有做用。

自定義詞庫後的分詞結果

7、寫在最後

中篇和下篇繼續肝,加油衝呀!

  • 中篇: 實戰 ES 應用。
  • 下篇: ES 的集羣部署。

你好,我是悟空哥「7年項目開發經驗,全棧工程師,開發組長,超喜歡圖解編程底層原理」
我還手寫了 2 個小程序Java 刷題小程序PMP 刷題小程序,點擊個人公衆號菜單打開!
另外有 111 本架構師資料以及 1000 道 Java 面試題,都整理成了PDF。
能夠關注公衆號 「悟空聊架構」 回覆 悟空 領取優質資料。

「轉發->在看->點贊->收藏->評論!!!」 是對我最大的支持!

我是悟空哥,努力變強,變身超級賽亞人!咱們下期見!

相關文章
相關標籤/搜索