elasticsearch 入門篇

weixin_33978044發表於2017-05-04

介紹

elasticsearch是一個高效的、可擴充套件的全文搜尋引擎

基本概念

  • Near Realtime(NRT): es是一個接近實時查詢平臺,意味從儲存一條資料到可以索引到資料時差很小,通常在1s內
  • Cluster: es是一個分散式、可擴充套件的平臺, 可由一個或多個伺服器通過定義的cluster.name(預設為elasticsearch)標識共建同一個叢集
  • Node: 通常一臺伺服器上部署一臺es node,作為叢集的一部分,用於資料的儲存和提供搜尋功能,在一個叢集中節點通過node.name區分,預設在node啟動時隨機生成一個的字串做為節點名稱,可配置
  • Index: 類似於關係型資料庫中的database,用於組織一類功能相似的資料,在一個叢集中可以定義任意個索引,索引的名稱只能由小寫字母組成,在資料索引,更新,搜尋,刪除時作為資料標識的一部分
  • Type: 類似於關係型資料庫中的table,在Index中可以定義多個Type,原則上一個Type是由相同屬性組成的資料集合
  • Document: 類似於關係型資料庫中的record,是資料的最基本儲存單元,使用json形式表示,Document在物理上儲存在Index下,但是在邏輯上會分配到具體的Type下
  • Shards & Replica:
    一個Index可能儲存大量的資料(超過單個節點的硬體限制),不管是資料儲存還是資料索引,為解決資料單節點儲存並提高併發,es將每一個Index物理分為多個片,從而水平擴充套件儲存容量,提高併發(可以同時對個shard進行索引和搜尋)
    為防止某個儲存單元出現故障後資料不能索引的情況,es提供將shard進行復制功能,將主shard出現故障後,複製shard替代主shard進行資料索引操作,已此方式實現其高可用性,因為在搜尋時可以使用複製shard,從而提高的資料搜尋的併發性
    在Index建立時可以進行分片數量和複製數量的設定,預設建立每個Index設定5個shard和1個Replica,表示該Index由5個邏輯儲存單元進行儲存,每個邏輯儲存單元具有一個複製節點進行備災,注意,shard只能在建立Index時進行設定,shard數量與document分配到哪個shard上儲存有關(通常使用hash(document _id) % shard num計算 document儲存在哪個shard上)
    在es將主shard和replic分片在不同的Node上

安裝

  • elasticsearch使用java語言實現,在使用時必須安裝java虛擬機器(目前es1.6和1.7版本均可選擇1.8版本java)
  • 下載地址
  • 解壓到安裝目錄 C:\Program Files\elasticsearch
  • 執行 cd "C:\Program Files\elasticsearch\bin" && elasticsearch.bat
  • 安裝到服務 service install elasticsearch
  • 啟動服務 net start elasticsearch
  • 停止服務 net stop elasticsearch
  • 測試
    訪問地址: http://localhost:9200
    訪問結果:
  {
    status: 200,
    name: "Smart Alec",
    cluster_name: "elasticsearch",
    version: {
      number: "1.6.0",
      build_hash: "cdd3ac4dde4f69524ec0a14de3828cb95bbb86d0",
      build_timestamp: "2015-06-09T13:36:34Z",
      build_snapshot: false,
      lucene_version: "4.10.4"
    },
    tagline: "You Know, for Search"
  }

介面

es對外提供標準RESTAPI介面,使用他進行叢集的所有操作:

  • 叢集、節點、索引的狀態和統計資訊檢視
  • 管理叢集、節點、索引和型別
  • 執行CURD操作(建立,更新,讀取,刪除)和索引
  • 執行高階搜尋功能,比如排序,分頁,篩選,聚合,js指令碼執行等

格式:curl -X<REST verb> <Node>:<Port>/<Index>/<Type>/<ID>

使用marvel外掛

  • 執行 cd "C:\Program Files\elasticsearch\bin" && plugin -i elasticsearch/marvel/latest
  • 訪問地址
  • marvel提供sense工具呼叫es的RESTAPI藉口, 訪問地址, 以下操作使用sense或使用linux curl命令列練習

狀態查詢

  • 叢集狀態查詢
    輸入: GET _cat/health?v
    輸出:
epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks
1442227489 18:44:49  elasticsearch yellow          1         1     50  50    0    0       50             0

說明:
status:表示叢集的健康狀態,值可能為green,yellow,red, green表示主shard和replica(至少一個)正常,yellow表示主shard正常但replica都不正常,red表示有的主shard和replica都有問題
node.total:表示叢集中節點的數量

  • 節點狀態查詢
    輸入: GET /_cat/nodes?v
    輸出:
host      ip             heap.percent ram.percent load node.role master name
silence   192.168.1.111            30          51      d         *      Thunderbird

查詢所有索引

輸入: GET /_cat/indices?v
輸出:

health status index              pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .marvel-2015.09.02   1   1      93564            0     78.4mb         78.4mb
yellow open   .marvel-2015.09.01   1   1      39581            0     45.9mb         45.9mb

建立索引

輸入: PUT /test1?pretty
輸出:

{
  "acknowledged" : true
}

查詢所有索引:

health status index              pri rep docs.count docs.deleted store.size pri.store.size
yellow open   test1                5   1          0            0       575b           575b

說明:
health:由於只執行一個節點,replica不能與主shard在同一node中,因此replica不正常,該index的狀態為yellow
index:為索引名稱
pri:表示主shard個數
rep:表示每個shard的複製個數
docs.count:表示index中document的個數

索引、讀取、刪除文件

索引文件

  • 方法1:
    輸入:
PUT /test1/user/1?pretty
{"name": "silence1"}

輸出:

{
  "_index" : "test1
  "_type" : "user",
  "_id" : "1",
  "_version" : 1,
  "created" : true
}
  • 方法2:
    輸入:
POST /test1/user/2?pretty
{"name": "silence2"}

輸出:

{
  "_index" : "test1",
  "_type" : "user",
  "_id" : "2",
  "_version" : 1,
  "created" : true
}
  • 方法3:
    輸入:
POST /test1/user?pretty
{"name": "silence3"}

輸出:

{
  "_index" : "test1",
  "_type" : "user",
  "_id" : "AU_MdQoXRYiHSIs7UGBQ",
  "_version" : 1,
  "created" : true
}

說明: 在索引文件時若需要指定文件ID值則需要使用PUT或者POST提交資料並顯示指定ID值,若需要由es自動生成ID,則需要使用POST提交資料

讀取文件:
輸入: GET /test1/user/1?pretty
輸出:

{
  "_index" : "test1",
  "_type" : "user",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source":{"name": "silence1"}
}

說明:
_index,_type:表示文件儲存的Index和Type資訊
_id:表示文件的編號
_version:表示文件的版本號,主要用於併發處理時使用樂觀鎖防止髒資料
found:表示請求的文件是否存在
_souce:格式為json,為文件的內容

注意:在之前我們並未建立user的Type,在進行文件索引時自動建立了user,在es中可以不顯示的建立Index和Type而使用預設引數或者根據提交資料自定義,但不建議這麼使用,在不清楚可能導致什麼情況時顯示建立Index和Type並設定引數

刪除文件:
輸入: DELETE /test1/user/1?pretty
輸出:

{
  "found" : true,
  "_index" : "test1",
  "_type" : "user",
  "_id" : "1",
  "_version" : 2
}

再次讀取文件輸出:

{
  "_index" : "test1",
  "_type" : "user",
  "_id" : "1",
  "found" : false
}

刪除索引

輸入: DELETE /test1?pretty
輸出:

{
  "acknowledged" : true
}

修改文件

初始化文件輸入:

PUT /test1/user/1?pretty
{"name" : "silence2", "age":28}

修改文件輸入:

PUT /test1/user/1?pretty
{"name" : "silence1"}

讀取文件輸出:

{
  "_index" : "test1",
  "_type" : "user",
  "_id" : "1",
  "_version" : 2,
  "found" : true,
  "_source":{"name" : "silence1"}
}

更新文件

更新資料輸入:

POST /test1/user/1/_update?pretty
{"doc" : {"name" : "silence3", "age":28}}

讀取資料輸出:

{
  "_index" : "test1",
  "_type" : "user",
  "_id" : "1",
  "_version" : 3,
  "found" : true,
  "_source":{"name":"silence3","age":28}
}

更新文件輸入:

POST /test1/user/1/_update?pretty
{"script" : "ctx._source.age += 1"}

讀取文件輸出:

{
  "_index" : "test1",
  "_type" : "user",
  "_id" : "1",
  "_version" : 4,
  "found" : true,
  "_source":{"name":"silence3","age":29}
}

說明:需要POST使用script則必須在elasticsearch/config/elasticsearch.yml配置script.groovy.sandbox.enabled: true
修改(PUT)和更新(POST+_update)的區別在於修改使用提交的文件覆蓋es中的文件,更新使用提交的引數值覆蓋es中文件對應的引數值

根據查詢刪除文件

輸入:

DELETE /test1/user/_query?pretty
{"query" : {"match" : {"name" : "silence3"}}}

輸出:

{
  "_indices" : {
    "test1" : {
      "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
      }
    }
  }
}

獲取文件數量

輸入: GET /test1/user/_count?pretty
輸出:

{
  "count" : 0,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  }
}

批量操作

輸入:

POST /test1/user/_bulk?pretty
{"index" : {"_id" : 1}}
{"name" : "silence1"}
{"index" : {"_id" : 2}}
{"name" : "silence2"}
{"index" : {}}
{"name" : "silence3"}
{"index" : {}}
{"name" : "silence4"}

輸入:

POST /test1/user/_bulk?pretty
{"update" : {"_id" : 1}}
{"doc" : {"age" : 28}}
{"delete" : {"_id" : 2}}

通過檔案匯入資料: curl -XPOST "localhost:9200/test1/account/_bulk?pretty" --data-binary @accounts.json

Query查詢

查詢可以通過兩種方式進行,一種為使用查詢字串進行提交引數查詢,一種為使用RESTAPI提交requesbody提交引數查詢

獲取所有文件輸入: GET /test1/user/_search?q=*&pretty

POST /test1/user/_search?pretty
{
  "query" : {"match_all" : {}}
}

輸出:

{
   "took": 2,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 3,
      "max_score": 1,
      "hits": [
         {
            "_index": "test1",
            "_type": "user",
            "_id": "1",
            "_score": 1,
            "_source": {
               "name": "silence1",
               "age": 28
            }
         },
         {
            "_index": "test1",
            "_type": "user",
            "_id": "AU_M2zgwLNdQvgqQS3MP",
            "_score": 1,
            "_source": {
               "name": "silence3"
            }
         },
         {
            "_index": "test1",
            "_type": "user",
            "_id": "AU_M2zgwLNdQvgqQS3MQ",
            "_score": 1,
            "_source": {
               "name": "silence4"
            }
         }
      ]
   }
}

說明:
took: 執行查詢的時間(單位為毫秒)
timed_out: 執行不能超時
_shards: 提示有多少shard參與查詢以及查詢成功和失敗shard數量
hits: 查詢結果
hits.total: 文件總數
_score, max_score: 為文件與查詢條件匹配度和最大匹配度

Query SDL

輸入:

POST /test1/account/_search?pretty
{
  "query" : {"match_all":{}},
  "size": 2,
  "from" : 6,
  "sort" : {
    "age" : {"order" : "asc"} 
  }
}

說明:
query: 用於定義查詢條件過濾
match_all: 表示查詢所有文件
size: 表示查詢返回文件數量,若未設定預設為10
from: 表示開始位置, es使用0作為開始索引,常與size組合進行分頁查詢,若未設定預設為0
sort: 用於設定排序屬性和規則

  • 使用_source設定查詢結果返回的文件屬性
    輸入:
POST /test1/account/_search?pretty
{
  "query": {
    "match_all": {}
  },
  "_source":["firstname", "lastname", "age"]
}

輸出:

{
   "took": 5,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 1000,
      "max_score": 1,
      "hits": [
         {
            "_index": "test1",
            "_type": "account",
            "_id": "4",
            "_score": 1,
            "_source": {
               "firstname": "Rodriquez",
               "age": 31,
               "lastname": "Flores"
            }
         },
         {
            "_index": "test1",
            "_type": "account",
            "_id": "9",
            "_score": 1,
            "_source": {
               "firstname": "Opal",
               "age": 39,
               "lastname": "Meadows"
            }
         }
      ]
   }
}
  • 使用match設定查詢匹配值
    輸入:
POST /test1/account/_search?pretty
{
  "query": {
    "match": {"address" : "986 Wyckoff Avenue"}
  },
  "size" : 2
}

輸出:

{
   "took": 1,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 216,
      "max_score": 4.1231737,
      "hits": [
         {
            "_index": "test1",
            "_type": "account",
            "_id": "4",
            "_score": 4.1231737,
            "_source": {
               "account_number": 4,
               "balance": 27658,
               "firstname": "Rodriquez",
               "lastname": "Flores",
               "age": 31,
               "gender": "F",
               "address": "986 Wyckoff Avenue",
               "employer": "Tourmania",
               "email": "rodriquezflores@tourmania.com",
               "city": "Eastvale",
               "state": "HI"
            }
         },
         {
            "_index": "test1",
            "_type": "account",
            "_id": "34",
            "_score": 0.59278774,
            "_source": {
               "account_number": 34,
               "balance": 35379,
               "firstname": "Ellison",
               "lastname": "Kim",
               "age": 30,
               "gender": "F",
               "address": "986 Revere Place",
               "employer": "Signity",
               "email": "ellisonkim@signity.com",
               "city": "Sehili",
               "state": "IL"
            }
         }
      ]
   }
}

說明:根據查詢結果可見在查詢結果中並非只查詢address包含"986 Wyckoff Avenue"的文件,而是包含986,wychoff,Avenue三個詞中任意一個,這就是es分詞的強大之處
可見查詢結果中_score(與查詢條件匹配度)按從大到小的順序排列
此時你可能想要值查詢address包含"986 Wyckoff Avenue"的文件,怎麼辦呢?使用match_phrase
輸入:

POST /test1/account/_search?pretty
{
  "query": {
    "match_phrase": {"address" : "986 Wyckoff Avenue"}
  }
}

可能你已經注意到, 以上query中只有一個條件,若存在多個條件,我們必須使用bool query將多個條件進行組合
輸入:

POST /test1/account/_search?pretty
{
  "query": {
    "bool" : {
      "must":[
        {"match_phrase": {"address" : "986 Wyckoff Avenue"}},
        {"match" : {"age" : 31}}  
      ]
    }
  }
}

說明: 查詢所有條件都滿足的結果

輸入:

POST /test1/account/_search
{
  "query": {
    "bool" : {
      "should":[
        {"match_phrase": {"address" : "986 Wyckoff Avenue"}},
        {"match_phrase": {"address" : "963 Neptune Avenue"}}
      ]
    }
  }
}

說明: 查詢有一個條件滿足的結果
輸入:

POST /test1/account/_search
{
  "query": {
    "bool" : {
      "must_not":[
        {"match": {"city" : "Eastvale"}},
        {"match": {"city" : "Olney"}}
      ]
    }
  }
}

說明: 查詢有條件都不滿足的結果

在Query SDL中可以將must, must_not和should組合使用
輸入:

POST /test1/account/_search
{
  "query": {
    "bool" : {
      "must": [{
        "match" : {"age":20}
      }],
      "must_not":[
        {"match": {"city" : "Steinhatchee"}}
      ]
    }
  }
}

Filters 查詢

在使用Query 查詢時可以看到在查詢結果中都有_score值, _score值需要進行計算, 在某些情況下我們並不需要_socre值,在es中提供了Filters查詢,它類似於Query查詢,但是效率較高,原因:

  1. 不需要對查詢結果進行_score值的計算
  2. Filters可以被快取在記憶體中,可被重複搜尋從而提高查詢效率
  • range 過濾器, 用於設定條件在某個範圍內
    輸入:
POST /test1/account/_search?pretty
{
  "query": {
    "filtered":{
      "query": {
        "match_all" : {}
      },
      "filter": {
        "range" : {
          "age" : {
            "gte" : 20,
            "lt" : 28
          }
        }
      }
    }
  }
}

判斷使用filter還是使用query的最簡單方法就是是否關注_score值,若關注則使用query,若不關注則使用filter

聚合分析

es提供Aggregations支援分組和聚合查詢,類似於關係型資料庫中的GROUP BY和聚合函式,在ES呼叫聚合RESTAPI時返回結果包含文件查詢結果和聚合結果,也可以返回多個聚合結果,從而簡化API呼叫和減少網路流量使用
輸入:

POST /test1/account/_search?pretty
{
  "size" : 0,
  "aggs" : {
    "group_by_gender" : {
      "terms" : {"field":"gender"}
    }
  }
}

輸出:

{
   "took": 1,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 1000,
      "max_score": 0,
      "hits": []
   },
   "aggregations": {
      "group_by_gender": {
         "doc_count_error_upper_bound": 0,
         "sum_other_doc_count": 0,
         "buckets": [
            {
               "key": "m",
               "doc_count": 507
            },
            {
               "key": "f",
               "doc_count": 493
            }
         ]
      }
   }
}

說明:
size: 返回文件查詢結果數量
aggs: 用於設定聚合分類
terms: 設定group by屬性值

輸入:

POST /test1/account/_search?pretty
{
  "size" : 0,
  "aggs" : {
    "group_by_gender" : {
      "terms" : {
        "field":"state",
        "order" : {"avg_age":"desc"},
        "size" : 3
      },
      "aggs" : {
        "avg_age" : {
          "avg" : {"field" : "age"}
        },
        "max_age" : {
          "max" : {"field": "age"}
        },
        "min_age" : {
          "min": {"field":"age"}
        }
      }
    }
  }
}

輸出:

{
   "took": 9,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 1000,
      "max_score": 0,
      "hits": []
   },
   "aggregations": {
      "group_by_gender": {
         "doc_count_error_upper_bound": -1,
         "sum_other_doc_count": 992,
         "buckets": [
            {
               "key": "de",
               "doc_count": 1,
               "max_age": {
                  "value": 37
               },
               "avg_age": {
                  "value": 37
               },
               "min_age": {
                  "value": 37
               }
            },
            {
               "key": "il",
               "doc_count": 3,
               "max_age": {
                  "value": 39
               },
               "avg_age": {
                  "value": 36.333333333333336
               },
               "min_age": {
                  "value": 32
               }
            },
            {
               "key": "in",
               "doc_count": 4,
               "max_age": {
                  "value": 39
               },
               "avg_age": {
                  "value": 36
               },
               "min_age": {
                  "value": 34
               }
            }
         ]
      }
   }
}

說明:根據state進行分類,並查詢每種分類所有人員的最大,最小,平均年齡, 查詢結果按平均年齡排序並返回前3個查詢結果
若需要按照分類總數進行排序時可以使用_count做為sort的field值
在聚合查詢時通過size設定返回的TOP數量,預設為10

在聚合查詢中可任意巢狀聚合語句進行查詢
輸入:

POST /test1/account/_search?pretty
{
  "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"},
          "aggs" : {
            "group_by_balance" :{
              "range" : {
                "field":"balance",
                "ranges" : [{
                  "to" : 5000
                }, {
                  "from" : 5000
                }
                ]
              }
            }
          }
        }
      }
    }
  }
}

輸出:

{
   "took": 1,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 1000,
      "max_score": 0,
      "hits": []
   },
   "aggregations": {
      "group_by_age": {
         "buckets": [
            {
               "key": "20.0-30.0",
               "from": 20,
               "from_as_string": "20.0",
               "to": 30,
               "to_as_string": "30.0",
               "doc_count": 451,
               "group_by_gender": {
                  "doc_count_error_upper_bound": 0,
                  "sum_other_doc_count": 0,
                  "buckets": [
                     {
                        "key": "m",
                        "doc_count": 232,
                        "group_by_balance": {
                           "buckets": [
                              {
                                 "key": "*-5000.0",
                                 "to": 5000,
                                 "to_as_string": "5000.0",
                                 "doc_count": 9
                              },
                              {
                                 "key": "5000.0-*",
                                 "from": 5000,
                                 "from_as_string": "5000.0",
                                 "doc_count": 223
                              }
                           ]
                        }
                     },
                     {
                        "key": "f",
                        "doc_count": 219,
                        "group_by_balance": {
                           "buckets": [
                              {
                                 "key": "*-5000.0",
                                 "to": 5000,
                                 "to_as_string": "5000.0",
                                 "doc_count": 20
                              },
                              {
                                 "key": "5000.0-*",
                                 "from": 5000,
                                 "from_as_string": "5000.0",
                                 "doc_count": 199
                              }
                           ]
                        }
                     }
                  ]
               }
            },
            {
               "key": "30.0-40.0",
               "from": 30,
               "from_as_string": "30.0",
               "to": 40,
               "to_as_string": "40.0",
               "doc_count": 504,
               "group_by_gender": {
                  "doc_count_error_upper_bound": 0,
                  "sum_other_doc_count": 0,
                  "buckets": [
                     {
                        "key": "f",
                        "doc_count": 253,
                        "group_by_balance": {
                           "buckets": [
                              {
                                 "key": "*-5000.0",
                                 "to": 5000,
                                 "to_as_string": "5000.0",
                                 "doc_count": 26
                              },
                              {
                                 "key": "5000.0-*",
                                 "from": 5000,
                                 "from_as_string": "5000.0",
                                 "doc_count": 227
                              }
                           ]
                        }
                     },
                     {
                        "key": "m",
                        "doc_count": 251,
                        "group_by_balance": {
                           "buckets": [
                              {
                                 "key": "*-5000.0",
                                 "to": 5000,
                                 "to_as_string": "5000.0",
                                 "doc_count": 21
                              },
                              {
                                 "key": "5000.0-*",
                                 "from": 5000,
                                 "from_as_string": "5000.0",
                                 "doc_count": 230
                              }
                           ]
                        }
                     }
                  ]
               }
            },
            {
               "key": "40.0-50.0",
               "from": 40,
               "from_as_string": "40.0",
               "to": 50,
               "to_as_string": "50.0",
               "doc_count": 45,
               "group_by_gender": {
                  "doc_count_error_upper_bound": 0,
                  "sum_other_doc_count": 0,
                  "buckets": [
                     {
                        "key": "m",
                        "doc_count": 24,
                        "group_by_balance": {
                           "buckets": [
                              {
                                 "key": "*-5000.0",
                                 "to": 5000,
                                 "to_as_string": "5000.0",
                                 "doc_count": 3
                              },
                              {
                                 "key": "5000.0-*",
                                 "from": 5000,
                                 "from_as_string": "5000.0",
                                 "doc_count": 21
                              }
                           ]
                        }
                     },
                     {
                        "key": "f",
                        "doc_count": 21,
                        "group_by_balance": {
                           "buckets": [
                              {
                                 "key": "*-5000.0",
                                 "to": 5000,
                                 "to_as_string": "5000.0",
                                 "doc_count": 0
                              },
                              {
                                 "key": "5000.0-*",
                                 "from": 5000,
                                 "from_as_string": "5000.0",
                                 "doc_count": 21
                              }
                           ]
                        }
                     }
                  ]
               }
            }
         ]
      }
   }
}

使用head外掛

  • 執行 cd "C:\Program Files\elasticsearch\bin" && plugin -install mobz/elasticsearch-head
  • 訪問地址

相關文章