1. 程式人生 > 程式設計 >探索ES-什麼?你還不知道ES?(一)

探索ES-什麼?你還不知道ES?(一)

前文回顧

之前有講過碎碎念-探索計劃(九),而且也陸續發了9篇關於Spring Boot的探索系列(當然遠遠還沒有結束)。一個是因為碎碎念-以終為始(四)中提到,我的其中一條目標是能夠做出一個有價值的產品,所以探索計劃也要擴充套件一名對未來能夠做出一個產品有幫助的新成員ES了。另外一個就是公司也使用到了ES,所以要加快輸入和輸出的腳步了。今天就來帶你從小白到入門ES

介紹

ES是一個搜尋引擎,底層基於LucceeElasticSearch剛開始是因為在倫敦的一個程式設計師要給老婆搜尋菜譜才開發出來的。東西被創造出來都是由需求驅動的,然後最後這個東西存在的意義是否還是最初的需求就不一定了。

環境

eskibana都是5.0.2版本,起碼今天是這個版本的。不過之後可能考慮升級到6.7版本。

安裝步驟

單節點部署

  1. 下載es和kibana的安裝包,解壓
  2. 修改配置檔案 elasticsearch.yml
  • cluster.name:my-application(當前叢集名稱)
  • node.name:node-1(當前節點名稱)
  • network.host:localhost(最好修改為本機IP地址,在叢集環境下localhost可能會有問題)
  • http.port:9200(ES埠)

叢集部署配置

  • discovery.zen.ping.unicast.host:["host1","host2"] (其他節點的IP地址和自己節點的地址)
  • dicovery.zen.minimum_master_nodes:(node_numbers / 2) + 1

修改JVM記憶體大小

  1. 修改jvm.options中記憶體大小 (考慮到效能不要設定太大,要為os cache預留一部分記憶體,保證磁碟上面的資料大小和os cache的大小是一致的,不懂沒有關係,後來慢慢講解)

啟動

  1. 啟動 bin/elasticsearch -d (-d表示用守護模式開啟)
  2. 訪問檢視ES狀態 curl http://localhost:9200 (有些情況下不能使用localhost,而是必須使用ip;看繫結的地址是什麼,如果顯示指定了IP,那麼必須是IP) 可以返回結果
{
  "name" : "node-1","cluster_name" : "my-application","cluster_uuid" : "0P_LVzBYS6qVl8Wo9o817Q","version" : {
    "number" : "5.0.2","build_hash" : "f6b4951","build_date" : "2016-11-24T10:07:18.101Z","build_snapshot" : false,"lucene_version" : "6.2.1"
  },"tagline" : "You Know,for Search"
}
複製程式碼

Kibana安裝

雖然可以不安裝Kibana,但是一般安轉完ElasticSearch都會安裝Kibana。(之後講解詳細安裝kibana詳細步驟)

檢視狀態

安裝完ES之後,一般人肯定會想要知道一些ES的狀態基本資訊,就好像安裝完資料庫之後,就想要知道這個資料庫安裝在哪個ip地址,裡面有多少張表,表佔用了多少空間等等基本資訊。那麼對於ES來說就是下面幾點。

檢視健康狀態

檢視健康狀態其實是和ES分片有沒有被分配有關係。存在主分片沒有被分配是Red。主分片已經被分配,但是副本分片沒有被分配是Yellow。剛開始學習最常見的Yellow情況就是ES只啟動了一個節點,副本分片不能和主分片在同一個節點上,導致副本分片無法被分配。 主分片和副本分片都分配的情況下,叢集才是Green。

GET _cat/health?v&pretty
複製程式碼

檢視節點狀態

看完了叢集的健康狀態之後,可以檢視每一個節點的詳細資訊,記憶體使用情況,cpu的使用情況等

GET /_cat/nodes?v
複製程式碼

參考 cat-nodes

檢視ES狀態全部索引

檢視索引的存在狀態

GET _cat/indices?v&pretty
複製程式碼

檢視ES節點配置資訊

GET _nodes?pretty
複製程式碼

檢視分片資訊

可以檢視某個特定索引的分片情況,狀態、在哪個節點等

GET _cat/shards/indexName*?pretty&v
複製程式碼

一般預設的好像不全面,可以使用下面這個語句。

GET _cat/shards?h=index,shard,prirep,state,unassigned.*&pretty&v
複製程式碼

通過這個語句可以看到分片級別的狀態,我發現ES有些API也不是很好。indices這個API都沒有說明沒有分片的個數。但是health裡面又存在整個叢集下沒有分片的個數。

分析分片沒有被分配原因

GET /_cluster/allocation/explain
{
  "index":"bank","shard":0,"primary":false
}
複製程式碼

基本概念

Node

節點。每個ES例項都是一個節點。可以對每個啟動的ES節點設定一個唯一的節點名稱,在elasticsearch.ymlnode.name欄位來指定。在ES中節點存在不同的型別,首先資料節點是儲存資料的。每次一個請求進來,因為每一個節點都擁有協調能力,在請求進來的時候,節點自動成為協調節點。

另外還有master節點和資料節點。master節點儲存了ES叢集的元資訊。當請求進入到master節點時,master節點根據hash演演算法和分片資訊,將請求打入到對應的分片上面。

Cluster

叢集。多個ES節點可以組成一個叢集。只要節點的叢集名字相同即可。在elasticsearch.ymlcluster.name來指定。只要cluster.name相同,那麼在節點之間網路連通的情況下,多個節點會自動組成一個叢集。

Index

index就是ES的索引,簡單理解為Mysql的資料庫。一個ES節點可以有多個索引。

Type

索引的型別。簡單理解為Mysql的表。對一個索引進行邏輯上面的區分。一個索引下面可以有多個type。

Document

檔案。相當於資料庫裡面的記錄。一個type下面可以多個檔案。

Shards And Replicas

分片和副本。一個索引預設會有5個分片和1個備份,會產生5個主分片和5個備份分片。5個分片會分散到叢集中不同的節點上面,另外需要注意的是主分片和副本分片一定不能再同一個節點上。如果有多個副本分片,相同資料的副本分片也不能在同一個節點上面。不然會導致分片沒有被分配。

Replica是ES實現高可用的一種方式,之後會進行深入的擴充套件。

也可能會遇到一些問題?比如ES沒有分配分片?之後會有文章會講到故障的排查。

建表、建立索引

跟資料庫一樣,要使用資料庫首先要建庫,在ES裡面就是建立索引。索引index相當於資料庫,type相當於表,id相當於檔案的主鍵。

PUT /people?pretty
複製程式碼

然後可以使用查詢全部索引的語句檢視,可以發現多了一條記錄。

ES與關係型資料庫不同的地方在於ES是可以動態增加欄位的。 所以,建索引(建表)的時候可以不指定所有的欄位,當有新的欄位存在時,可以使用動態對映的方式(dymatic mapping)來指定欄位型別。

要注意的點是一個新的欄位是什麼型別只有在第一次這個欄位進去到ES時會被指定,其他時候不會被指定。

檢視節點狀態能夠看到只是yellow,因為當前只有一個節點,副本分片不能和主分片在同一個節點上,導致副本分片沒有被分配,沒有實現高可用。

epoch      timestamp cluster        status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1554208915 20:41:55  my-application yellow          1         1      6   6    0    0        6             0                  -                 50.0%
複製程式碼
  • docs.count:表示該索引下面所有的檔案數

插入ES操作

有了索引之後,可以使用Restful介面插入資料到ES中。使用下面的語句建立。

PUT /people/info/1?pretty
{
  "name": "John Doe"
}
複製程式碼

這裡的1就是建立的新檔案的id,可以指定也可以不指定。建議不指定,會快一些。成功之後提示如下。

{
  "_index": "people","_type": "info","_id": "1","_version": 1,"result": "created","_shards": {
    "total": 2,"successful": 1,"failed": 0
  },"created": true
}
複製程式碼

這裡使用的是PUT指令,表示更新指定的檔案。因為是更新指定的檔案,所以這裡第三個引數1是必須填寫的。ES的更新檔案的操作其實是會判斷檔案存不存在,如果不存在那麼就是新建一條新的檔案。如果想要直接真正新建一條檔案,而且這種情況下也不需要填寫id,可以直接新增,可以使用POST方式,如下。

POST /people/info?pretty
{
  "name": "John Doe"
}
複製程式碼

返回格式如下。

{
  "_index": "people","_id": "AWnjMpZuDCZ3hLyrI_RF","created": true
}
複製程式碼

還可以使用bulk來批量插入資料,如下所示。要注意的是bulk對於json的格式有嚴格的要求,每一個完整的json只能是一行,不能多行。而已一個完整json之後必須換行。

bulk還可以這樣寫

POST /customer/external/_bulk?pretty
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
複製程式碼

ES查詢

那麼現在測試資料都進入了ES,如何查詢呢?一般人可能會有兩種查詢需求,一種是查詢某個索引下面所有的資料,一種是根據id來查詢,另外就是根據某一個具體的欄位來查詢。一個一個來看。 首先所有的查詢都必須加上_search欄位在請求的末尾。

  • 查詢索引下面全部的檔案。只要在需要查詢的index後面加上_search就好了。
GET /people/_search
複製程式碼

也可以這樣寫。

GET /people/_search
{
  "query": { "match_all": {} }
}
複製程式碼

返回如下。

{
  "took": 2,"timed_out": false,"_shards": {
    "total": 5,"successful": 5,"hits": {
    "total": 6,"max_score": 1,"hits": [
      {
        "_index": "people","_score": 1,"_source": {
          "name": "John Doe"
        }
      },.....省略後面5條資料
複製程式碼
  • 根據某一個欄位來查詢,使用match查詢。
GET /people/_search
{
  "query": {
    "match": {
      "name": "John"
    }
  }
}
複製程式碼

這裡就是搜尋所有name中含有John的檔案。這裡的含有就是指定的欄位必須是全文索引的。 但是想要精確地搜尋nameJohn的檔案。就可以使用term查詢,而不是match查詢。

POST _search
{
  "query": {
    "term" : { "name" : "John" } 
  }
}
複製程式碼

ES更新檔案

PUT /people/info/1?pretty
{
  "name": "John Doe"
}
複製程式碼

通過上面這個更新的語句就可以將檔案更新。但是這裡重點要講一下,這個更新操作是覆蓋更新。在ES的底層,更新操作會先找到這條ES資料,然後用新的ES資料給完全覆蓋掉這條資料。

但是往往有時候我們想要實現的是增量更新,那麼應該怎麼辦呢?

PUT /people/info/1/_update
{
"doc":{
"name":"Mike"
}
}
複製程式碼

通過這種方式就可以將某一個檔案增量更新了。但是如果要刪除某一個檔案裡面的一個欄位,應該只能用全量的方式了吧。注意這裡的語法一定要加上doc,url上面要加上_update。

查詢全部檔案

GET _search
{
  "query": {
    "match_all": {}
  }
}
複製程式碼

所有的命令可以總結為<REST Verb> /<Index>/<Type>/<ID>

排序

POST /_search
{
   "query" : {
      "term" : { "product" : "chocolate" }
   },"sort" : [
      {"price" : {"order" : "asc"}}
   ]
}
複製程式碼

聚合操作

ES不僅提供了資料的搜尋還提供了資料的檢索,聚合。下面就來聊一聊聚合操作。

簡單聚合

先來個簡單的聚合操作。 首先下載app.json的原始資料。需求是按state來分組計算出分組中document的數量,然後計算分組內金額的平均數。size:0表示不會返回命中的document,只是返回分組,在ES中分組又被稱之為桶bucket,預設返回10個桶。

GET /bank/_search
{
  "size": 0,"aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },"aggs": {
        "average_balance77": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
複製程式碼

得到如下結果

{
  "took": 75,"timed_out": false,"_shards": {
    "total": 5,"successful": 5,"failed": 0
  },"hits": {
    "total": 1000,"max_score": 0,"hits": []//size:0,所示hits不展示
  },"aggregations": {
    "group_by_state": {
      "doc_count_error_upper_bound": 20,"sum_other_doc_count": 770,"buckets": [//第一個bucket
        {
          "key": "ID","doc_count": 27,"average_balance77": {//balance平均數
            "value": 24368.777777777777
          }
        },{//第二個bucket
          "key": "TX","average_balance77": {//balance平均數
            "value": 27462.925925925927
          }
        },複製程式碼

巢狀聚合

看過簡單的聚合之後,是不是迫不及待想要看看複雜的聚合操作呢?先上例子。

GET /bank/_search
{
  "size": 0,"aggs": {
    "group_by_age": {
      "range": {
        "field": "age","ranges": [
          {
            "from": 20,"to": 30
          },{
            "from": 30,"to": 40
          },{
            "from": 40,"to": 50
          }
        ]
      },"aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender.keyword"
          },"aggs": {
            "average_balance": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      }
    }
  }
}
複製程式碼

其實就是聚合之後可以再次對於已經聚合之後的資料進行聚合操作。就好像sql中group by之後,再對group by之後的資料再次進行group by操作。分桶之後可以再次分桶。

小結

好了,看完了上文,關於ES的安裝、部署、查詢、更新、聚合等基本操作都掌握了嗎?

關於寫作

以後這裡每天都會寫一篇文章,題材不限,內容不限,字數不限。儘量把自己每天的思考都放入其中。

如果這篇文章給你帶來了一些幫助,可以動動手指點個贊,順便關注一波就更好了。

如果上面都沒有,那麼寫下讀完之後最想說的話?有效的反饋和你的鼓勵是對我最大的幫助。

另外打算把部落格給重新撿起來了。歡迎大家來訪問吃西瓜

我是shane。今天是2019年8月16日。百天寫作計劃的第二十三天,23/100。