一、ES簡介
1,什麼是ES
ElasticSearch是一個基於Lucene的搜尋伺服器。它提供了一個分散式的全文搜尋引擎,其對外服務是基於RESTful web介面釋出的。Elasticsearch是用Java開發的應用,並作為Apache許可條款下的開放原始碼釋出,是當前流行的企業級搜尋引擎。設計用於雲端計算中,能夠達到近實時搜尋,穩定,可靠,快速,安裝使用方便。
2,ES的相關概念
a)cluster
cluster叢集。ElasticSearch叢集由一或多個節點組成,其中有一個主節點,這個主節點是可以通過選舉產生的,主從節點是對於叢集內部來說的。ElasticSearch的一個概念就是去中心化,字面上理解就是無中心節點,這是對於叢集外部來說的,因為從外部看ElasticSearch叢集,在邏輯上是個整體,你與叢集中的任何一個節點通訊和與整個ElasticSearch叢集通訊是等價的。也就是說,主節點的存在不會產生單點安全隱患、併發訪問瓶頸等問題。
b)shards
primary shard:代表索引的主分片,ElasticSearch可以把一個完整的索引分成多個primary shard,這樣的好處是可以把一個大的索引拆分成多個分片,分佈儲存在不同的ElasticSearch節點上,從而形成分散式儲存,併為搜尋訪問提供分散式服務,提高併發處理能力。primary shard的數量只能在索引建立時指定,並且索引建立後不能再更改primary shard數量(重新分片需要重新定義分片規則)。es5.x之後預設為5,es7.x預設為1。
c)replicas
replica shard:代表索引主分片的副本,ElasticSearch可以設定多個replica shard。replica shard的作用:一是提高系統的容錯性,當某個節點某個primary shard損壞或丟失時可以從副本中恢復。二是提高ElasticSearch的查詢效率,ElasticSearch會自動對搜尋請求進行負載均衡,將併發的搜尋請求傳送給合適的節點,增強併發處理能力。可取值為0~n,預設為1。
d)Index
索引。相當於關係型資料庫中的表。其中儲存若干相似結構的Document資料。如:客戶索引,訂單索引,商品索引等。ElasticSearch中的索引不像資料庫表格一樣有強制的資料結構約束,在理論上,可以儲存任意結構的資料。但了為更好的為業務提供搜尋資料支撐,還是要設計合適的索引體系來儲存不同的資料。
e)Type
型別。每個索引中都必須有唯一的一個Type,Type是Index中的一個邏輯分類。ElasticSearch中的資料Document是儲存在索引下的Type中的。
注意:ElasticSearch5.x及更低版本中,一個Index中可以有多個Type。ElasticSearch6.x版本之後,type概念被弱化,一個index中只能有唯一的一個type。且在7.x版本之後,刪除type定義。
f)Document
文件。ElasticSearch中的最小資料單元。一個Document就是一條資料,一般使用JSON資料結構表示。每個Index下的Type中都可以儲存多個Document。一個Document中可定義多個field,field就是資料欄位。如:學生資料({"name":"張三", "age":20, "gender":"男"})。
g)反向索引(倒排索引)
對資料進行分析,抽取出資料中的詞條,以詞條作為key,對應資料的儲存位置作為value,實現索引的儲存。這種索引稱為倒排索引。倒排索引是Document寫入ElasticSearch時分析維護的。
3,比資料庫做搜尋的優勢
- 資料庫查詢複雜度高。比如:like '%關鍵字%'不能命中索引,搜尋複雜度高
- 資料庫關鍵字的搜尋不全面,搜尋結果不符合要求。比如:搜尋商品為'膝上型電腦',不能搜尋到只有'筆記本'或者只有'電腦'的資料
- 資料庫搜尋的效率問題。資料量越大,查詢效率越低。
二、ElasticSearch索引操作
1,查詢健康狀態
GET _cat/health?v
其中status的狀態分為三種:green、yellow和red
- green:每個索引的primary shard和replica shard都是active的
- yellow:每個索引的primary shard都是active的,但部分的replica shard不是active的。比如:當前只有兩個node結點,需要建立大於等於兩個repica shard副分片,由於主分片和副分片均不能在同一個結點上,所有必定有副分片不能正常的active。
- red:不是所有的索引的primary shard都是active狀態的。
#檢視健康狀態 GET _cat/health?v #檢視節點資訊 GET _cat/nodes?v #檢視索引資訊 GET _cat/indices?v #檢視分片資訊 GET _cat/shards?v
2,建立索引
#建立my_index索引(settings可以省略),建立後shards分片數不能修改,只能修改shards副本數 PUT my_index { "settings": { "number_of_shards": 5, "number_of_replicas": 1 } }
在ElasticSearch中,預設的建立索引的時候,會分配5個primary shard,併為每個primary shard分配一個replica shard(在ES7版本後,預設建立1個primary shard)。在ElasticSearch中,預設的限制是:如果磁碟空間不足15%的時候,不分配replica shard。如果磁碟空間不足5%的時候,不再分配任何的primary shard。ElasticSearch中對shard的分佈是有要求的。ElasticSearch儘可能保證primary shard平均分佈在多個節點上。Replica shard會保證不和他備份的那個primary shard分配在同一個節點上。
3,修改索引(副分片數)
#修改索引 PUT my_index/_settings { "number_of_replicas": 2 }
注意:索引一旦建立,primary shard數量不可變化,可以改變replica shard數量。
4,刪除索引
DELETE my_index
5,檢視索引資訊
GET _cat/indices?v
三、ElasticSearch中Document相關操作
1,新增Document
在索引中增加文件。在index中增加document。
ElasticSearch有自動識別機制。如果增加的document對應的index不存在,自動建立index;如果index存在,type不存在,則自動建立type。如果index和type都存在,則使用現有的index和type。
a)PUT
PUT 索引名/型別名/唯一ID{欄位名:欄位值}
#如果當前id已經存在,那麼就是修改,如果不存在就是新增
PUT my_index/_doc/1 { "name":"test_doc_01", "remark":"first test elastic search", "order_no":1 }
如果當前索引中的document的id已經存在,那麼就是修改,如果不存在就是新增。但是如果此時id已經存在,想要強制新增會報錯,強制新增的語法為:
PUT 索引名/型別名/唯一ID/_create{欄位名:欄位值} 或者是 PUT 索引名/型別名/唯一ID?op_type=create{欄位名:欄位值}
b)POST
此操作為ElasticSearch自動生成id的新增Document方式。此語法格式和PUT請求的資料新增,只有唯一的區別,就是可以自動生成主鍵id,其他的和PUT請求新增資料完全一致。
POST 索引名/型別名[/唯一ID]{欄位名:欄位值}
#此時,如果新增時唯一id(2)不存在就是新增,如果唯一id(2)存在就是修改。這個與PUT相同
#可以直接變為沒有id,會隨機生成一個GUID作為id
POST my_index/_doc { "name":"test_doc_02", "remark":"second test elastic search", "order_no":2 }
2,查詢Document
a)GET 通過ID查詢
GET 索引名/型別名/唯一ID
GET my_index/_doc/1
b)GET _mget批量查詢
批量查詢可以提高查詢效率。推薦使用(相對於單資料查詢來說)。
#語法 GET 索引名/型別名/_mget { "docs" : [ { "_id" : "唯一ID值" }, { "_id" : "唯一ID值" } ] }
3,修改Document
a)全量替換(同新增)
PUT|POST 索引名/型別名/唯一ID{欄位名:欄位值}
本操作相當於覆蓋操作。全量替換的過程中,ElasticSearch不會真的修改Document中的資料,而是標記ElasticSearch中原有的Document為deleted狀態,再建立一個新的Document來儲存資料,當ElasticSearch中的資料量過大時,ElasticSearch後臺回收deleted狀態的Document。
PUT my_index/_doc/1 { "name":"test_doc_01111", "remark":"first 111", "order_no":1111 }
b)更新Document
POST 索引名/型別名/唯一ID/_update{doc:{欄位名:欄位值}}
只更新某Document中的部分欄位。這種更新方式也是標記原有資料為deleted狀態,建立一個新的Document資料,將新的欄位和未更新的原有欄位組成這個新的Document,並建立。對比全量替換而言,只是操作上的方便,在底層執行上幾乎沒有區別。
POST my_index/_doc/1/_update { "doc":{ "name":" test_doc_01_for_update" } }
4,刪除Document
DELETE 索引名/型別名/唯一ID
ElasticSearch中執行刪除操作時,ElasticSearch先標記Document為deleted狀態,而不是直接物理刪除。當ElasticSearch儲存空間不足或工作空閒時,才會執行物理刪除操作。標記為deleted狀態的資料不會被查詢搜尋到。
DELETE my_index/_doc/2
5,bulk批量增刪改
定義:
POST _bulk { "action_type" : { "metadata_name" : "metadata_value" } } { document datas | action datas } action_type: create: 強制建立,相當於PUT 索引名/型別名/唯一ID/_create index : 普通的PUT操作,相當於建立Document或全量替換 update: 更新操作(partial update),相當於 POST 索引名/型別名/唯一ID/_update delete: 刪除操作
案例:
#如果index和type為同一個可以提出來,此時建立ID為111,覆蓋ID為1,修改ID為2,刪除ID為3 POST my_index/_doc/_bulk {"create":{"_id":111}} {"name":"zs","age":15} {"index":{"_id":1}} {"name":"first","sort":1} {"update":{"_id":2}} {"doc":{"sort":2}} {"delete":{"_id":3}}
注意:bulk語法中要求一個完整的json串不能有換行。不同的json串必須使用換行分隔。多個操作中,如果有錯誤情況,不會影響到其他的操作,只會在批量操作返回結果中標記失敗。bulk語法批量操作時,bulk request會一次性載入到記憶體中,如果請求資料量太大,效能反而下降(記憶體壓力過高),需要反覆嘗試一個最佳的bulk request size。一般從1000~5000條資料開始嘗試,逐漸增加。如果檢視bulk request size的話,一般是5~15MB之間為好。
bulk語法要求json格式是為了對記憶體的方便管理,和儘可能降低記憶體的壓力。如果json格式沒有特殊的限制,ElasticSearch在解釋bulk請求時,需要對任意格式的json進行解釋處理,需要對bulk請求資料做json物件會json array物件的轉化,那麼記憶體的佔用量至少翻倍,當請求量過大的時候,對記憶體的壓力會直線上升,且需要jvm gc程式對垃圾資料做頻繁回收,影響ElasticSearch效率。
生成環境中,bulk api常用。都是使用java程式碼實現迴圈操作。一般一次bulk請求,執行一種操作。如:批量新增10000條資料等。
四、ElasticSearch中的mapping
Mapping在ElasticSearch中是非常重要的一個概念。決定了一個index中的field使用什麼資料格式儲存,使用什麼分詞器解析,是否有子欄位等。
1,mapping的核心資料型別
- 文字(字串):text
- 整數:byte、short、integer、long
- 浮點型:float、double
- 布林型別:boolean
- 日期型別:date
- 陣列型別:array {a:[]}
- 物件型別:object {a:{}}
- 不分詞的字串(關鍵字): keyword
2,dynamic mapping自動分配欄位型別
- true or false -> boolean
- 123 -> long
- 123.123 -> double
- 2018-01-01 -> date
- hello world -> text
- [] -> array
- {} -> object
在上述的自動mapping欄位型別分配的時候,只有text型別的欄位需要分詞器。預設分詞器是standard分詞器。
3,檢視索引mapping
GET 索引名/_mapping
{ "my_index": { # 索引名 "mappings": { # 對映列表 "my_type": { # 型別名 "properties": { # 欄位列表 "age": { # 欄位名 "type": "long" # 欄位型別 }, "gender": { #欄位名 "type": "text", #欄位型別 "fields": { # 子欄位列表 "keyword": { # 子欄位名 "type": "keyword", # 子欄位型別,keyword不進行分詞處理的文字型別。gender.keyword可以進行排序 "ignore_above": 256 # 子欄位儲存資料長度 } } } } } } } }
4,custom mapping
可以通過命令,在建立index和type的時候來定製mapping對映,也就是指定欄位的型別和欄位資料使用的分詞器。
手工定製mapping時,只能新增mapping設定,不能對已有的mapping進行修改。
如:有索引a,其中有型別b,增加欄位f1的mapping定義。後續可以增加欄位f2的mapping定義,但是不能修改f1欄位的mapping定義。
a)建立索引時指定mapping
PUT 索引名稱 { "mappings":{ "型別名稱":{ "properties":{ "欄位名":{ "type":型別, ["analyer":欄位的分詞器,] ["fields":{ "子欄位名稱":{ "type":型別, "ignore_above":長度限制 } }] } } } } }
例如:
PUT test_index { "settings": { "number_of_shards": 2, "number_of_replicas": 1 }, "mappings": { "test_type":{ "properties": { "author_id" : { "type": "byte", "index": false }, "title" : { "type": "text", "analyzer": "ik_max_word", "fields": { "keyword" : { "type": "keyword", "ignore_above": 256 } } }, "content" : { "type": "text", "analyzer": "ik_max_word" }, "post_date" : { "type": "date" } } } } } "index" - 是否可以作為搜尋索引。可選值:true | false "analyzer" - 指定分詞器。 "type" - 指定欄位型別
b)為已有索引新增新的欄位mapping
PUT 索引名/_mapping/型別名 { "properties":{ "新欄位名":{ "type":型別, "analyer":欄位的分詞器, "fields":{ "子欄位名":{ "type":型別, "ignore_above":長度 } } } }
例如:
PUT /test_index/_mapping/test_type { "properties" : { "new_field" : { "type" : "text" , "analyzer" : "standard" } } }
c)測試不同的欄位分詞器
GET 索引名稱/_analyze { "field":"索引中的text型別的欄位名", "text":"要分詞處理的文字資料" }
例如:
#測試content欄位的分詞效果
GET test_index/_analyze { "field": "content", "text": "我是一個程式設計師" }