ElasticSearch 學習筆記(一) 基本概念與基本使用

北冥有隻魚 發表於 2022-07-18
ElasticSearch
一般我介紹某個框架、MQ、中介軟體,一般都是講是啥,能幫助我們幹啥,然後用起來,高階特性。這次打算換一種風格,穿插一些小故事。寫到這篇的時候,我想起我剛入行的第一個專案,有一個頁面查詢,主表兩百七十萬條資料,join了七張表,第二張從表一百多萬資料,剩餘五張大概在四五十萬條資料的上下,查詢大概十幾秒,這樣的速度肯定是難以滿足要求的,請DBA優化,優化到七秒,使用者勉強可以接受了,我當時問帶我的大哥,如果資料量再往上漲,更慢怎麼辦,我當時的大哥說,可以考慮ElasticSearch,ElasticSearch號稱億級資料,光速查詢。

緣起

許多年前,一個剛結婚的名叫 Shay Banon 的失業開發者,跟著他的妻子去了倫敦,他的妻子在那裡學習廚師。 在尋找一個賺錢的工作的時候,為了給他的妻子做一個食譜搜尋引擎,他開始使用 Lucene 的一個早期版本。

直接使用 Lucene 是很難的,因此 Shay 開始做一個抽象層,Java 開發者使用它可以很簡單的給他們的程式新增搜尋功能。 他釋出了他的第一個開源專案 Compass。

後來 Shay 獲得了一份工作,主要是高效能,分散式環境下的記憶體資料網格。這個對於高效能,實時,分散式搜尋引擎的需求尤為突出, 他決定重寫 Compass,把它變為一個獨立的服務並取名 Elasticsearch。

第一個公開版本在2010年2月釋出,從此以後,Elasticsearch 已經成為了 Github 上最活躍的專案之一,他擁有超過300名 contributors(目前736名 contributors )。 一家公司已經開始圍繞 Elasticsearch 提供商業服務,並開發新的特性,但是,Elasticsearch 將永遠開源並對所有人可用。

據說,Shay 的妻子還在等著她的食譜搜尋引擎… 《Elasticsearch: 權威指南》基於2.x 版本

那Lucene是啥,Lucene是一個全文搜尋引擎庫,屬於Apache,全稱為Apache Lucene,Luece可以說是當下最先進、高效能全功能的搜尋引擎庫, 但是Lucene僅僅只是一個庫,使用起來比較複雜。Shay Banon構建了一個抽象層,試圖對開發者遮蔽複雜細節,Java開發者使用它可以很簡單的給他們的程式新增搜尋功能,這也就是Compass,最後演變為ElasticSearch。

演進

全文搜尋? 傳統的資料庫不行嗎? 《ElasticSearch權威指南》給出的理由是:

不幸的是,大部分資料庫在從你的資料中提取可用知識時出乎意料的低效。 當然,你可以通過時間戳或精確值進行過濾,但是它們能夠全文檢索、處理同義詞、通過相關性給文件評分麼? 它們能從同樣的資料中生成分析與聚合資料嗎?最重要的是,它們能實時地做到上述操作,而不經過大型批處理的任務麼?

從上面的這句話我們可以提取的有效資訊是,大部分關係資料不能夠進行全文檢索、處理同義詞、通過相關性給文件進行評分。

  • 什麼是全文檢索(搜尋)?

Full Text Searching (or just text search) provides the capability to identify natural-language documents that satisfy a query, and optionally to sort them by relevance to the query. The most common type of search is to find all documents containing given query terms and return them in order of their similarity to the query. Notions of query and similarity are very flexible and depend on the specific application. The simplest search considers query as a set of words and similarity as the frequency of query words in the document. [1]

全文搜尋(或文字搜尋)提供識別滿足查詢自然語言的能力,並且根據查詢結果進行相關性進行排序。全文搜尋最為常見的是從所有文件中找出滿足條件的所有文件,並根據相似度進行排序。 注意查詢和相似是非常靈活和依賴於具體應用的。最為簡單的全文搜尋是將“查詢”視為一組詞,將相似性當作查詢次的頻率。

  • 那什麼是Document?

A document is the unit of searching in a full text search system; for example, a magazine article or email message. The text search engine must be able to parse documents and store associations of lexemes (key words) with their parent document. Later, these associations are used to search for documents that contain query words.

Document是全文搜尋系統的基本單元,比如雜誌上的一篇文章或者一封郵件均可以被視為文件。全文搜尋引擎一定要能夠解析文件,並儲存文件的關鍵詞和文件的關聯。這些關聯將會被用來搜尋包含查詢此的文件。

上面的描述是不是跟我們平時用的搜尋引擎有點類似呢,我們在搜尋引擎框輸入關鍵詞,搜尋引擎搜尋整個Web,搜尋引擎根據相關性進行網頁排名。比如我在百度搜尋Zookeeper,下面是百度的搜尋結果:

百度的搜尋結果

我在bing進行搜尋:

bing的搜尋結果

這些網頁是搜尋引擎根據相關性排出來的。百度還處理了同義詞,Zookeeper有動物園管理員的意思,所以動物園管理員也被帶了出來。再比如apple官網和蘋果官網就是同義詞,你在百度輸入這兩個關鍵詞,第一個都是蘋果公司的官網。上面提到的這些在傳統的關係型資料庫去實現都是難以做到的,但是Elasticsearch就能為我們提供。其實這裡還涉及一個分詞的問題,舉一個例子,我在搜尋引擎上搜尋ES分詞,對於搜尋引擎來說,這是一個輸入,他去匹配文件的時候,事實上是將ES分詞,當成兩個詞:ES 分詞。文件中包含這兩個詞來計算相關性。既然你能為我們做這麼多,那就只能,omg,學它。

以上功能都很像是搜尋引擎的功能,所以ElasticSearch 將自己定位為:

Elasticsearch 是一個分散式、RESTful 風格的搜尋和資料分析引擎,能夠解決不斷湧現出的各種用例,適用於包括文字、數字、地理空間、結構化和非結構化資料等在內的所有型別的資料.

ES 當前最新的版本是8.3, 我們不選最新的,我們基於7.17這個版本來進行介紹。Linux伺服器基於Centos 7

基本概念

ES概念

既然是ElstaticSearch可以充當搜尋引擎,那麼它們的資料來源是哪裡呢,ElstaticSearch有內建的資料來源, 在某種形式可以理解為另一種形式的資料庫。

Index

看到這個詞,我下意識的想到了MySQL的索引,但是在ES裡,索引有兩個含義:

  • 名詞: 一個索引相當於關係型資料庫中的一個表(在6.x以前,一個index可以被認為是一個資料庫)
  • 動詞: 將一份document儲存在一個index裡,這個過程也可以稱為索引。

既然一個index 相當於關係型資料庫的一個表,那ElstaticSearch有對應的建表語句嗎?有的那就是Mapping。

Mapping

Mapping定義了索引裡的文件到底有哪些欄位及這些欄位的型別,類似於資料庫中表結構的定義。Mapping有兩種作用:

  • 定義了索引中各個欄位的名稱和對應的型別。
  • 定義了各個欄位、倒排索引的相關設定,如使用什麼分詞器等。

需要注意的是,Mapping一旦定義後,已經定義的欄位型別是不可更改的

type

在6.x之前,index可以被理解為關係型資料庫中的資料庫,而type則被認為是資料庫中的表。使用type允許我們在index裡面儲存多種型別的資料,資料篩選時,可以指定type。type的存在從某種程度上可以減少index的數量,但是type存在以下限制:

  • 不同 type 裡的欄位需要保持一致。例如,一個 index 下的不同 type 裡有兩個名字相同的欄位,他們的型別(string, date 等等)和配置也必須相同。
  • 只在某個 type 裡存在的欄位,在其他沒有該欄位的 type 中也會消耗資源。
  • 得分是由 index 內的統計資料來決定的。也就是說,一個 type 中的文件會影響另一個 type 中的文件的得分。

以上限制要求我們,只有同一個 index 的中的 type 都有類似的對映 (mapping) 時,才勉強適用 type 。否則,使用多個 type 可能比使用多個 index 消耗的資源更多。

6.00 之後開始逐步廢棄type, 開始不支援一個index裡面存在多個type。

7.00版本後正式廢除單個索引下多Type的支援。

Document

Elasticsearch is a distributed document store. Instead of storing information as rows of columnar data, Elasticsearch stores complex data structures that have been serialized as JSON documents

ElasticSearch儲存文件的方式是分散式儲存,與儲存列式資料不同,ES儲存的是序列為JSON的文件。

上面已經講了一些基本概念了,下面我們將ElasticSearch裝起來,在實踐中介紹其他核心概念。

裝起來

安裝ES

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.0-linux-x86_64.tar.gz
mkdir es
mv elasticsearch-7.17.0-linux-x86_64.tar.gz  es
# ES 7 以後自帶JDK,你在解壓過程中會發現ES自帶的JDK有module這個概念,已經不是JDK8嘍
# 已經是JDK 17了
tar xvf elasticsearch-7.17.0-linux-x86_64.tar.gz 
# 為後面搭建叢集做準備
mv elasticsearch-7.17.0  es_node1

ES 目錄大致介紹

目錄描述
bin包含一些執行指令碼,其中 ES 的啟動檔案和指令碼安裝檔案就在這裡
config包含叢集的配置檔案(elasticsearch.yml)、jvm配置(jvm.options)、user等相關配置
JDK7.0 後自帶 jdk,java 執行環境
libjava 的類庫
plugins外掛安裝的目錄
modules包含所有 ES 的模組

修改配置

雖然ES號稱開箱即用,但我們還是要改一些配置的,ES的配置檔案主要在config目錄下,我們主關注的是兩個目錄:

  • elasticsearch.yml
elasticsearch.yml 是用來配置 ES 服務的各種引數的
  • jvm.options
jvm.options 主要儲存 JVM 相關的配置。
echo -e '\n' >> config/elasticsearch.yml
# 向yml追加配置
echo 'cluster.name: my_app' >> config/elasticsearch.yml

echo 'node.name: my_node_1' >> config/elasticsearch.yml

echo 'path.data: ./data' >> config/elasticsearch.yml

echo 'path.logs: ./logs' >> config/elasticsearch.yml

echo 'http.port: 9211' >> config/elasticsearch.yml
# 允許任意ip 直連,真實環境不要設定為0.0.0.0
echo 'network.host: 0.0.0.0' >> config/elasticsearch.yml

echo 'discovery.seed_hosts: ["localhost"]' >> config/elasticsearch.yml

echo 'cluster.initial_master_nodes: ["my_node_1"]' >> config/elasticsearch.yml

echo -e '\n' >> config/jvm.options

# 設定堆記憶體最小值

echo '-Xms1g' >> config/jvm.options

# 設定堆記憶體最大值

echo '-Xmx1g' >> config/jvm.options

sudo su

echo -e '\nvm.max_map_count=262144' >> /etc/sysctl.conf

sysctl -p

exit;

執行ES

# 注意,ES 不能使用 root 來執行!!!! 這裡我們新建個使用者
adduser es_study
# 這裡我將密碼設定為my_studyes001
passwd  es_study 
# 將esnode1 這個資料夾授予給es_study
chown -R es_study es_node1/
# 該limits.conf  
# 追加 * soft nofie 65536  
# 追加 * hard nofile 65536
vim /etc/security/limis.conf 
# 然後重新登入,如果下面命令輸出65536代表生效
ulimit -H -n
# 切換使用者
su es_study
# 到bin目錄下 
 ./elasticsearch

然後訪問ES所在主機ip:9211, 出現下面:

{
  "name" : "my_node_1",
  "cluster_name" : "my_app",
  "cluster_uuid" : "G6WNl_15TFmJ6VhDJIXfCw",
  "version" : {
    "number" : "7.17.0",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "bee86328705acaa9a6daede7140defd4d9ec56bd",
    "build_date" : "2022-01-28T08:36:04.875279988Z",
    "build_snapshot" : false,
    "lucene_version" : "8.11.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

說明安裝成功, 因為我配置了環境變數, 所以用的還是JDK 8, 但是這次我想讓ES 用JDK 17.

# 在/etc/profile 中加入ES_JAVA_HOME即可
vim /etc/profile
source /etc/profile
export ES_JAVA_HOME=/usr/es/es_node1/jdk

安裝Kibana

Kibana 是官方的資料分析和視覺化平臺,但現在我們只需要把它當作 ES 查詢的除錯工具即可。 Kibana 與 ES 的版本是有對應關係的,所以需要下載與 ES 同版本的 Kibana。

# 注意kibana 也不能在root使用者下面執行,我們還是要用上面的使用者來啟動 授予許可權
wget https://artifacts.elastic.co/downloads/kibana/kibana-7.17.0-linux-x86_64.tar.gz
tar xvf kibana-7.17.0-linux-x86_64.tar.gz
mv kibana-7.17.0-linux-x86_64 kibana
cd kibana
# 需要注意的是,線上一定不能配置ip為 0.0.0.0,這是非常危險的行為!!!
echo -e '\nserver.host: "0.0.0.0"' >> config/kibana.yml
echo -e '\nelasticsearch.hosts: ["http://localhost:9211"]' >> config/kibana.yml
chown -R es_study kibana
./bin/kibana >> run.log 2>&1 &

然後訪問ES所在主機ip:5601, 看到如下圖所示:

選擇DevTools

用起來

費了那麼大勁,終於裝好了, 現在我們在Kibana中用起來。上面我們提到ES是一個分散式、RESTful 風格的搜尋和資料分析引擎,能夠解決不斷湧現出的各種用例。考慮有的同學還不知道RESTFul風格,這裡簡單介紹一下, HTTP有若干種請求方式,讓請求方式和CRUD能對的上:

  • POST 新增
  • PUT 更新
  • DELETE 刪除
  • GET 獲取資源

對索引進行管理-Mapping

  • 建立索引
PUT books
{
  "mappings": {
    "properties": {
      "book_id": {
        "type": "keyword" 
      },
      "name": {
        "type": "text"
      },
         "price":{
        "type":"scaled_float",
         "scaling_factor": 100
      },
      "author": { 
       "properties": {
          "first": { "type": "text" },
          "last":  { "type": "text" }
       }  
    }
  }
}
}   

上面定義了一個通過ElasticSearch的Mapping簡單定義了一個索引, type代表資料型別,ES常見的資料型別有:

  • keyword
keyword 型別更適合儲存簡短、結構化的字串。例如產品ID、產品名字等
  • text
text型別的欄位適合儲存全文字資料,如簡訊內容、郵件內容等。
  • 數字型別(long, integer, short, byte, double, float, half_float, scaled_float...)

    • 就整數型別而言(byte short integer long)而言,應當選擇滿足要求的最小型別
    • 對於浮點型別來說, 使用縮放因子將浮點資料儲存到整數中通常更為有效,這也就是我們上面用到的scaled_float. 如果輸入時23.45,ES會將輸入的價格是23.456 乘以100, 再取一個接近原始值的數字,得出2346。使用比例因子的好處是整型比浮點型更易壓縮,節省空間,節省磁碟空間。
  • 物件型別
簡單來說就是欄位的值還是一個json物件。

我們登上Kibana的DevTools輸入上面的Mapping命令:

建立成功

  • 刪除索引
DELETE books

對文件的增刪改查

  • create

    • INDEX API 建立

      這種方式指定了文件的API

    PUT books/_doc/1 
    {
      "book_id": "4ee82462",
      "name": "母豬的產後護理(一)",
      "price":"23.56",  
      "author": {"first":"wang","last":"小明"}
    }

    如果在Kibana中連續執行,不會報錯,也不會重複建立,版本號會發生改變:

    ES的版本號

    在ES中索引一個文件的時候,如果文件ID已經存在,會先刪除舊文件,然後再寫入新文件的內容,並且增加文件的版本號。

    • 建立文件(create doc)

      POST books/_doc
      {
        "book_id": "4ee824621",
        "name": "母豬的產後護理(二)",
        "price":"23.56",  
        "author": {"first":"wang","last":"小明"}
      }

      使用post方式建立文件由ES生成ID,連續執行上面的命令,會發現版本號沒發生改變,說明是連續新增。

  • QUERY

    • 查詢ID 為1的文件
    GET books/_doc/1

    GET API 提供了多個引數, 更多的資訊請參考官方文件,

    • 批量查詢
    # 引數中指定index
    GET /_mget
    {
      "docs": [
        { "_index": "books", "_id": "1" },
        { "_index": "books", "_id": "00IDB4IBeGTeMZoVN5XN" }
      ]
    }
    # 在URL中指定index
    GET /_mget
    {
      "docs": [
        { "_index": "books", "_id": "1" },
        { "_index": "books", "_id": "00IDB4IBeGTeMZoVN5XN" }
      ]
    }
    # 簡寫
    GET /books/_mget
    {
      "ids" : ["1", "00IDB4IBeGTeMZoVN5XN"]
    }
  • update
POST books/_update/1
{
  "doc": {
    "name":"偉大的傅立葉變換"
  }
}

上面的索引文件也能更新,那跟這種方式的更新由什麼區別呢,索引文件的更新效果是先刪除資料,然後再寫入舊資料。如果我們只想更新一個欄位,並只給了更新的欄位,那麼其他欄位就會被抹去。

  • delete

    在引數上指定id即可。

DELETE books/_doc/1

那麼如果我們想批量操縱索引中的文件呢?ElstaticSearch為我們提供了Bulk API 操縱批量處理文件。Bulk API中可以同時支援4種型別的操縱:

  • Index
  • Create
  • Update
  • Delete

語法示例:

POST _bulk
{ "index" : { "_index" : "books", "_id" : "3" } }  #指定操縱型別 哪個索引和文件
{ "book_id": "4ee82462","name":"天生吾戰"}
{ "delete":{"_index": "books", "_id":"2"}}
{"update":{"_index":"books","_id":3}}
{"doc":{"name":"母豬的產後護理(四)"}}

歷程

一般我還是先去ElasticSearch的官網去看文件,ES有中文官網,但是漢化了一部分,ES官網放的有《Elasticsearch: 權威指南》,可惜是基於2.x版本:

高版本放的都有使用者指南:

選DOCS

使用者指導

本文的安裝教程基本上應用了掘金小冊《Elasticsearch 從入門到實踐》

參考文件