公號:碼農充電站pro
主頁:https://codeshellme.github.iohtml
本節介紹 ES 文檔,索引及其基本操做。python
在 ES 中,文檔(Document)是可搜索數據的最小存儲單位,至關於關係數據庫中的一條記錄。git
文檔以 Json 數據格式保存在 ES 中,Json 中保存着多個鍵值對,它能夠保存不一樣類型的數據,好比:github
Python 語言中的字典類型,就是 Json 數據格式。shell
文檔中的數據類型能夠指定,也能夠由 ES 自動推斷。數據庫
每一個文檔中都有一個 Unique ID,用於惟一標識一個文檔。Unique ID 能夠由用戶指定,也能夠由 ES 自動生成。json
Unique ID 其實是一個字符串。api
好比下面的 Json 就是一個文檔:數組
{ "name" : "XiaoMing", "age" : 19, "gender" : "male" }
將上面那個 Json 數據存儲到 ES 後,會像下面這樣:安全
{ "_index": "person", "_type": "_doc", "_id": "2344563", "_version": 1, "_source": { "name": "XiaoMing", "age": 19, "gender": "male" } }
其中如下劃線開頭的字段就是元數據:
_index
:文檔所屬的索引。_type
:文檔的類型。ES 7.0 開始,一個索引只能有一種 _type
。_id
:文檔的惟一 ID。_source
:文檔的原始 Json 數據。_version
:文檔更新的次數。你能夠查看這裏,瞭解「爲何單個Index下,再也不支持多個Tyeps?」。
更多關於元數據的信息,能夠參考這裏。
ES 中文檔的刪除操做不會立刻將其刪除,而是會將其標記到 del 文件中,在後期合適的時候(好比 Merge 階段)會真正的刪除。
ES 中的文檔是不可變動的,更新操做會將舊的文檔標記爲刪除,同時增長一個新的字段,而且文檔的 version 加 1。
在 ES 中,一個文檔默認最多能夠有 1000 個字段,能夠經過 index.mapping.total_fields.limit 進行設置。
注意在設計 ES 中的數據結構時,不要使文檔的字段數過多,這樣會使得 mapping 很大,增長集羣的負擔。
ES 中的文檔都會存儲在某個索引(Index)中,索引是文檔的容器,是一類文檔的集合,至關於關係型數據庫中的表的概念。
ES 中能夠建立不少不一樣的索引,表示不一樣的文檔集合。
每一個索引均可以定義本身的 Mappings 和 Settings:
Mappings
:用於設置文檔字段的類型。Settings
:用於設置不一樣的數據分佈。對於索引的一些參數設置,有些參數能夠動態修改,有些參數在索引建立後不能修改,可參考這裏。
ES 與傳統數據庫類比
若是將 ES 中的基本概念類比到傳統數據庫中,它們的對應關係以下:
ES | 傳統數據庫 |
---|---|
索引 | 表 |
文檔 | 行 |
字段 | 列 |
Mapping | 表定義 |
DSL | SQL 語句 |
索引相關 API
下面給出一些查看索引相關信息的 API:
# 查看索引相關信息 GET index_name # 查看索引的文檔總數 GET index_name/_count # 查看指定索引的前10條文檔 POST index_name/_search { } #_cat indices API # 查看全部的索引名以 index_prefix 爲前綴的索引 GET /_cat/indices/index_prefix*?v&s=index # 查看狀態爲 green 的索引 GET /_cat/indices?v&health=green # 按照文檔個數排序 GET /_cat/indices?v&s=docs.count:desc # 查看指定索引的指定信息 GET /_cat/indices/index_prefix*?pri&v&h=health,index,pri,rep,docs.count,mt # 查看索引使用的內存大小 GET /_cat/indices?v&h=i,tm&s=tm:desc
GET 操做能夠獲取指定文檔的內容。
GET index_name/_count
:獲取指定索引中的文檔數。
GET index_name/_doc/id
:獲取指定索引中的指定文檔。
GET index_name/_doc
:不容許該操做。
GET index_name
:獲取指定索引的 Mappings
和 Settings
。
POST/PUT 操做用於建立文檔。
按照 POST / PUT 方法來區分
POST index_name/_doc
:
POST index_name/_doc
:不指定 ID,老是會插入新的文檔,文檔數加 1。POST/PUT index_name/_doc/id
:指定 ID
PUT index_name/_create
:
PUT index_name/_create
:不指定 ID,不容許該操做。PUT index_name/_create/id
:指定 ID
PUT index_name/_doc
:
PUT index_name/_doc
:不指定 ID,不容許該操做。PUT/POST index_name/_doc/id
:指定 ID
PUT index_name/_doc/id?op_type=XXX
op_type=create
:
op_type=index
:
按照是否指定 ID 來區分
指定 ID:
POST/PUT index_name/_doc/id
:指定 ID,稱爲 Index 操做
PUT index_name/_doc/id?op_type=index
PUT index_name/_doc/id?op_type=create
:指定 ID,稱爲 Create 操做
PUT index_name/_create/id
不指定 ID:
POST index_name/_doc
:不指定 ID,老是會插入新的文檔,文檔數加 1。PUT index_name/_doc
:不指定 ID,不容許該操做。PUT index_name/_create
:不指定 ID,不容許該操做。Update 操做用於更新文檔的內容。
POST index_name/_update/id/
:更新指定文檔的內容。更新的內容要放在 doc 字段中,不然會報錯。
實際上 ES 中的文檔是不可變動的,更新操做會將舊的文檔標記爲刪除,同時增長一個新的字段,而且文檔的 version 加 1。
Delete 操做用於刪除索引或文檔。
DELETE /index_name/_doc/id
:刪除某個文檔。
not_found
。DELETE /index_name
:刪除整個索引,要謹慎使用!
404
錯誤。批量操做指的是,在一次 API 調用中,對不一樣的索引進行屢次操做。
每次操做互不影響,即便某個操做出錯,也不影響其餘操做。
返回的結果中包含了全部操做的執行結果。
Bulk 支持的操做有 Index
,Create
,Update
,Delete
。
Bulk 操做的格式以下:
POST _bulk { "index" : { "_index" : "test", "_id" : "1" } } { "field1" : "value1" } { "delete" : { "_index" : "test", "_id" : "2" } } { "create" : { "_index" : "test2", "_id" : "3" } } { "field1" : "value3" } { "update" : {"_id" : "1", "_index" : "test"} } { "doc" : {"field2" : "value2"} }
注意 Bulk 請求體的數據量不宜過大,建議在 5~15M。
Mget 一次讀取多個文檔的內容,設計思想相似 Bulk 操做。
Mget 操做的格式以下:
GET _mget { "docs" : [ {"_index" : "index_name1", "_id" : "1"}, {"_index" : "index_name2", "_id" : "2"} ] }
也能夠在 URI 中指定索引名稱:
GET /index_name/_mget { "docs" : [ {"_id" : "1"}, {"_id" : "2"} ] }
還能夠用 _source
字段來設置返回的內容:
GET _mget { "docs" : [ {"_index" : "index_name1", "_id" : "1"}, {"_index" : "index_name2", "_id" : "2", "_source" : ["f1", "f2"]} ] }
Msearch 操做用於批量查詢,格式以下:
POST index_name1/_msearch {} # 索引名稱,不寫的話就是 URI 中的索引 {"query" : {"match_all" : {}},"size":1} {"index" : "index_name2"} # 改變了索引名稱 {"query" : {"match_all" : {}},"size":2}
URI 中也能夠不寫索引名稱,此時請求體裏必須寫索引名稱:
POST _msearch {"index" : "index_name1"} # 索引名稱 {"query" : {"match_all" : {}},"size":1} {"index" : "index_name2"} # 索引名稱 {"query" : {"match_all" : {}},"size":2}
上文中介紹了 3 種批量操做,分別是 Bulk,Mget,Msearch。注意在使用批量操做時,數據量不宜過大,避免出現性能問題。
當咱們的請求發生錯誤的時候,ES 會返回相應的錯誤碼,常見的錯誤碼以下:
錯誤碼 | 含義 |
---|---|
429 | 集羣過於繁忙 |
4XX | 請求格式錯誤 |
500 | 集羣內部錯誤 |
有時候咱們須要重建索引,好比如下狀況:
mappings
發生改變:好比字段類型或者分詞器等發生更改。settings
發生改變:好比索引的主分片數發生更改。ES 中提供兩種重建 API:
先在一個索引中插入數據:
DELETE blogs/ # 寫入文檔 PUT blogs/_doc/1 { "content":"Hadoop is cool", "keyword":"hadoop" } # 查看自動生成的 Mapping GET blogs/_mapping # 查詢文檔 POST blogs/_search { "query": { "match": { "content": "Hadoop" } } } # 能夠查到數據
如今修改 mapping(添加子字段是容許的),爲 content 字段加入一個子字段:
# 修改 Mapping,增長子字段,使用英文分詞器 PUT blogs/_mapping { "properties" : { "content" : { # content 字段 "type" : "text", "fields" : { # 加入一個子字段 "english" : { # 子字段名稱 "type" : "text", # 子字段類型 "analyzer":"english" # 子字段分詞器 } } } } } # 查看新的 Mapping GET blogs/_mapping
修改 mapping 以後再查詢文檔:
# 使用 english 子字段查詢 Mapping 變動前寫入的文檔 # 查不到文檔 POST blogs/_search { "query": { "match": { "content.english": "Hadoop" } } } # 注意:不使用 english 子字段是能夠查詢到以前的文檔的 POST blogs/_search { "query": { "match": { "content": "Hadoop" } } }
結果發現,使用 english 子字段是查不到以前的文檔的。這時候就須要重建索引。
下面使用 Update by query
對索引進行重建:
# Update全部文檔 POST blogs/_update_by_query { }
重建索引以後,不論是使用 english 子字段仍是不使用,均可以查出文檔。
Update by query
操做還能夠設置一些條件:
request-body 示例:
POST tech_blogs/_update_by_query?pipeline=blog_pipeline { "query": { # 將 query 的查詢結果進行重建 "bool": { "must_not": { "exists": {"field": "views"} } } } }
在原有 mapping 上,修改字段類型是不容許的:
# 會發生錯誤 PUT blogs/_mapping { "properties" : { "content" : { "type" : "text", "fields" : { "english" : { "type" : "text", "analyzer" : "english" } } }, "keyword" : { # 修改 keyword 字段的類型 "type" : "keyword" } } }
這時候只能建立一個新的索引,設置正確的字段類型,而後再將原有索引中的數據,重建到新索引中。
創建一個新的索引 blogs_new:
# 建立新的索引而且設定新的Mapping PUT blogs_new/ { "mappings": { "properties" : { "content" : { "type" : "text", "fields" : { "english" : { "type" : "text", "analyzer" : "english" } } }, "keyword" : { "type" : "keyword" } } } }
下面使用 Reindex 將原來索引中的數據,導入到新的索引中:
# Reindx API POST _reindex { "source": { # 指定原有索引 "index": "blogs" }, "dest": { # 指定目標索引 "index": "blogs_new" } }
Reindex API 中的 source 字段和 dest 字段還有不少參數能夠設置,具體可參考其官方文檔。
另外 Reindex 請求的 URI 中也能夠設置參數,能夠參考這裏。
同一個資源在多併發處理的時候,會發生衝突的問題。
傳統數據庫(好比 MySQL)會採用鎖的方式,在更新數據的時候對數據進行加鎖,來防止衝突。
而 ES 並無採用鎖,而是將併發問題交給了用戶處理。
在 ES 中能夠採用兩種方式:
if_seq_no
和 if_primary_term
version
和 version_type=external
示例,首先插入數據:
DELETE products PUT products/_doc/1 { "title":"iphone", "count":100 } # 上面的插入操做會返回 4 個字段: #{ # "_id" : "1", # "_version" : 1, # "_seq_no" : 0, # "_primary_term" : 1 #}
使用內部版本控制的方式:
PUT products/_doc/1?if_seq_no=0&if_primary_term=1 { "title":"iphone", "count":100 } # 上面的更新操做返回下面內容: #{ # "_id" : "1", # "_version" : 2, # 加 1 # "_seq_no" : 1, # 加 1 # "_primary_term" : 1 # 不變 #}
若是再次執行這句更新操做,則會出錯,出錯以後由用戶決定如何處理,這就達到了解決衝突的目的。
# 再執行則會出錯,由於 seq_no=0 且 primary_term=1 的數據已經不存在了 PUT products/_doc/1?if_seq_no=0&if_primary_term=1
先看下數據庫中的數據:
GET products/_doc/1 # 返回: { "_index" : "products", "_type" : "_doc", "_id" : "1", # id "_version" : 2, # version "_seq_no" : 1, "_primary_term" : 1, "found" : true, "_source" : { "title" : "iphone", "count" : 100 } }
使用外部版本控制的方式:
# 若是 URI 中的 version 值與 ES 中的 version 值相等,則出錯 # 下面這句操做會出錯,出錯以後,由用戶決定如何處理 PUT products/_doc/1?version=2&version_type=external { "title":"iphone", "count":1000 } # 若是 URI 中的 version 值與 ES 中的 version 值不相等,則成功 # 下面這句操做會成功 PUT products/_doc/1?version=3&version_type=external { "title":"iphone", "count":1000 }
Ingest 節點用於對數據預處理,它是在 ES 5.0 後引入的一種節點類型,能夠達到必定的 Logstash 的功能。
默認狀況下,全部的節點都是 Ingest 節點。
Ingest 節點經過添加一些 processors 來完成特定的處理,Pipeline 能夠看作是一組 processors 的順序執行。
Ingest 節點的處理階段以下圖所示:
ES 中內置了不少現成的 Processors 供咱們使用:
ES 中提供了一個 simulate 接口,用於測試 Processors。
示例:
POST _ingest/pipeline/_simulate { "pipeline": { # 定義 pipeline "description": "to split blog tags", # 描述 "processors": [ # 一系列的 processors { "split": { # 一個 split processor "field": "tags", "separator": "," # 用逗號分隔 } }, { "set":{ # 能夠設置多個 processor "field": "views", "value": 0 } } ] }, "docs": [ # 測試的文檔 { # 第 1 個文檔 "_index": "index", "_id": "id", "_source": { "title": "Introducing big data......", "tags": "hadoop,elasticsearch,spark", "content": "You konw, for big data" } }, { # 第 2 個文檔 "_index": "index", "_id": "idxx", "_source": { "title": "Introducing cloud computering", "tags": "openstack,k8s", "content": "You konw, for cloud" } } ] }
當 Processors 測試經過後,能夠向 ES 中添加(設置)一個 Pipeline,語法:
# blog_pipeline 爲 pipeline 名稱 PUT _ingest/pipeline/blog_pipeline { "description": "a blog pipeline", "processors": [ { "split": { # 第 1個 Processor "field": "tags", "separator": "," } }, { "set":{ # 第 2個 Processor "field": "views", "value": 0 } } ] }
# 查看 Pipleline GET _ingest/pipeline/blog_pipeline # 刪除 Pipleline DELETE _ingest/pipeline/blog_pipeline
# blog_pipeline 是 Pipeline 名稱 POST _ingest/pipeline/blog_pipeline/_simulate { "docs": [ { # 一個文檔 "_source": { "title": "Introducing cloud computering", "tags": "openstack,k8s", "content": "You konw, for cloud" } } ] }
使用 Pipeline 插入文檔時,文檔會先通過 Pipeline 的處理,而後再插入到 ES 中。
# URI 中指定了 Pipeline 的名字 PUT tech_blogs/_doc/2?pipeline=blog_pipeline { "title": "Introducing cloud computering", "tags": "openstack,k8s", "content": "You konw, for cloud" }
最終插入的文檔是這樣的:
{ "title": "Introducing cloud computering", "tags": ["openstack", "k8s"], "content": "You konw, for cloud", "views": 0 }
另外 update-by-query(重建索引)的 URI 中也能夠設置 pipeline 參數來使用一個 Pipeline。
上文介紹到的全部操做,能夠參考 ES 的官方文檔。
(本節完。)
推薦閱讀:
Kibana,Logstash 和 Cerebro 的安裝運行
歡迎關注做者公衆號,獲取更多技術乾貨。