elasticsearch 索引_Elasticsearch系列---索引管理
概要
Elasticsearch讓索引創建變得非常簡單,只要索引一條新的數據,索引會自動創建出來,但隨著數據量的增加,我們開始有了索引優化和搜索優化的需求之后,就會發現自動創建的索引在某些方面不能非常完美的適應我們的需求,我們開始考慮手動創建適合我們業務需求的索引。
索引的CRUD
為了更好地貼切我們的業務數據需求,我們開始更精細的管理我們的索引。
創建索引
創建索引的語法示例如下:
PUT /music {"settings": {"number_of_shards": 3,"number_of_replicas": 1},"mappings": {"children": {"properties": {"name": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}}}}} }settings內的參數
- numberofshards:每個索引的primary shard數量,索引創建后不可修改。
- numberofreplicas: 每個索引的replica shard的數量,可以隨時修改。
mappings內的參數
- type: 6.3.1版本只允許設置一個type
- properties:類型映射具體信息,索引文檔的字段名稱,類型,分詞器都在里面指定。
默認Elasticsearch是允許自動創建索引的,生產環境上為了避免自動索引可能出現的隱患,可以禁止自動創建索引,修改elasticsearch.yml配置文件即可:
action.auto_create_index: false
修改索引
可以單獨修改setting部分和mapping部分,修改setting部分示例如下:
PUT /music/_settings {"number_of_replicas": 2 }如果要修改mapping信息,如給索引新增字段length、likes、content,示例如下:
PUT /music/_mapping/children {"properties": {"length": {"type": "long"},"likes": {"type": "long"},"content": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}}} }刪除索引
DELETE /music DELETE /music,content DELETE /music* DELETE /_all DELETE /*如上命令均可刪除索引,但此操作一定要慎重,反復確認后再操作,誤刪的后果不可想像,建議刪除操作一定要設置操作權限,另外Elasticsearch可以設置只限定索引名稱進行刪除,不允許通配符或_all刪除大量的索引,作如下設置即可:
action.destructive_requires_name: true
誤刪索引的后果非常嚴重,請在操作權限上加把鎖,寧可麻煩也不要誤刪。
查看索引信息
GET /music GET /music/_settings GET /music/_mapping三條命令可以查看索引的完整信息,只查setting信息,只查mapping信息。
分詞器設置
analysis是索引設置中非常重要的一部分,默認的分詞器我們前面有介紹,有興趣可以翻一下。我們可以為索引單獨配置特有的分詞器,或者自定義分詞器。
修改分詞器設置
例如,我們為music索引創建一個新的分詞器,叫做music_std,啟用英文停用詞列表:
PUT /music {"settings": {"analysis": {"analyzer": {"music_std": {"type": "standard","stopwords": "_english_"}}}} }此命令只能在創建時候執行,已經存在的索引執行會報錯。
我們對music索引進行分詞器測試:
GET /music/_analyze {"analyzer": "music_std","text": "get up brightly early in the morning" }測試結果是"in","the"這兩個詞已經被正確的移除掉了。
自定義分詞器
Elasticsearch對分詞器的應用設置得非常靈活,用戶可以根據自己的需求靈活定制字符過濾器、分詞器、詞單元過濾器來創建自定義的分詞器。
文檔的分詞過程包含以下幾步:
- 字符過濾器
對字符串進行預處理,如HTML標簽清洗Love --> Love,I & you --> I and you等等。
- 分詞器
把字符串切分成單個的詞條,如英文的按空格和標點切分,中文的按詞語切分,針對不同的語言,有不同的分詞器,有相對簡單的標準分詞器,也有特別復雜的中文分詞器,里面包含了非常復雜的切分邏輯如:
I Love you --> I/Love/you
我和我的祖國 --> 我/和/我的/祖國
- Token過濾器
將分詞器得到的詞條進一步的處理,如改變詞條(英文詞干提取loves --> love),刪除無實際意義的詞條(英文的a, and, this,中文的"的","了","嗎"),增加詞條(補充同義詞)
如果我們自定義分詞器,可以從這三個組件入手,可以自行替換。我們舉一個示例:
PUT /music {"settings": {"analysis": {"char_filter": {"&_to_and": {"type": "mapping","mappings": ["&=> and"]}},"filter": {"my_stopwords": {"type": "stop","stopwords": ["the", "a"]}},"analyzer": {"my_analyzer": {"type": "custom","char_filter": ["html_strip", "&_to_and"],"tokenizer": "standard","filter": ["lowercase", "my_stopwords"]}}}} }上面示例中我們自定義的分詞器有如下特點:
- 字符過濾器:把&轉換成and,并加上html_strip處理html文本
- token過濾器:將"the","a"作為停用詞,全部改成小寫
我們對這個分詞器進行測試:
GET /music/_analyze {"text": "you & me the love, <a>, HAHA!!","analyzer": "my_analyzer" }響應的結果:
{"tokens": [{"token": "you","start_offset": 0,"end_offset": 3,"type": "<ALPHANUM>","position": 0},{"token": "and","start_offset": 4,"end_offset": 5,"type": "<ALPHANUM>","position": 1},{"token": "me","start_offset": 6,"end_offset": 8,"type": "<ALPHANUM>","position": 2},{"token": "love","start_offset": 13,"end_offset": 17,"type": "<ALPHANUM>","position": 4},{"token": "haha","start_offset": 24,"end_offset": 28,"type": "<ALPHANUM>","position": 5}] }可以看到,"the"作為停用詞被移除了,&變成了"and",html標簽移除了,HAHA小寫處理后得到haha。
自定義分詞器后,如果需要應用在索引上,需要將它綁定到具體的字段上:
PUT /music/_mapping/children {"properties": {"content": {"type": "text","analyzer": "my_analyzer"}} }后面只要有新的文檔進行索引,在content字段上都會使用我們自定義的分詞器。
映射對象
root object
映射對象信息是一組JSON結構,最頂層的叫根對象(root object),包括內容如下:
- properties: 索引中每個字段的映射信息。
- metadata:各種元數據信息,以下劃線開頭,如id,source,_type。
- settings:設置項信息,如analyzer。
- 其他settings:比如includeinall
properties
主要是指文檔字段和屬性最重要的三個設置:
- type: 數據類型,如text、date、long等。
- index: 該字段是否需要全文搜索(analyzed),或精準搜索(not_analyzed)或是不支持搜索(no)。
- analyzer: 文檔索引和搜索時的分詞器。
例如節選了以下properties信息:
{"music": {"mappings": {"children": {"properties": {"author": {"type": "text","analyzer": "english"}}}}} }_source
source字段存儲的內容包含文檔的JSON字符串,source字段在寫入磁盤前會被壓縮。
_source存儲的內容才是我們真正關心的數據,我們可以更加方便的完成這些事:
- 查詢的時候可以一次性拿到完整的document,不需要先拿document id,再發送一次請求拿document
- partial update基于_source實現
- reindex時,直接基于_source實現,不需要從數據庫(或者其他外部存儲)查詢數據再修改
- 可以基于_source定制返回field
- debug query更容易,因為可以直接看到_source
_all
建立索引時將所有field拼接在一起,作為一個all field ,沒指定任何field進行搜索時,就是搜索all field,一般輕量搜索中用得比較多。
如果不需要_all field,可以設置成禁用:
PUT /music/_mapping/children {"_all": {"enabled": false} }也可以指定某些field不加入_all field
PUT /music/_mapping/children {"properties": {"author": {"type": "text","include_in_all": false}} }metadata
文檔標識主要的幾個字段:
- _id:文檔ID
- _type:類型名稱,6.x以后一個索引只會有一個type
- _index: 文檔所在的索引名稱
這三個字段是用來標識一個獨一無二的文檔所在的位置信息,從這三個字段我們基本上可以定位出來該文檔存儲在哪個shard中。
動態映射
dynamic屬性
Elasticsearch索引文檔時,如果JSON結構出現新的字段,Elasticsearch會根據dynamic mapping規則來識別字段的數據類型,并自動增加新的字段,如果我們對文檔的JSON結構有較嚴格的規定,這種自動增加字段的行為,就不是我們期望的操作,我們可以為properties設置dynamic屬性來決定這種行為:
- true: 動態添加新的字段
- false:忽略新的字段
- strict: 遇到新的字段,拋出異常
這個dynamic參數可以在任何一層的object中使用,如:
PUT /music {"mappings": {"children": {"dynamic": "strict","properties": {"name": {"type": "text"},"address": {"type": "object","dynamic": "true"}}}} }如果children下面遇到新字段,就會拋出異常如果address內部對象中遇到新字段,會動態創建該字段
示例:
# address內部對象增加兩個新字段 PUT /music/children/1 {"name":"sunshine","address": {"province": "gd","city": "sz"} }創建成功,響應如下:
{"_index": "music","_type": "children","_id": "1","_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 0,"_primary_term": 1 } # children下直接增加新字段author PUT /music/children/1 {"name":"sunshine","author":"Johnny Cash" }創建失敗,報錯響應如下:
{"error": {"root_cause": [{"type": "strict_dynamic_mapping_exception","reason": "mapping set to strict, dynamic introduction of [author] within [children] is not allowed"}],"type": "strict_dynamic_mapping_exception","reason": "mapping set to strict, dynamic introduction of [author] within [children] is not allowed"},"status": 400 }定制dynamic mapping策略
Elasticsearch在運行中遇到新增的字段時,會根據動態映射模板為新的字段定義類型,但字段類型是根據首次遇到的字段值來定義的,可能會出現誤判的情況。
我們先舉一個反例,假設我們有一個新增的字段remark,里面的內容是"2019-12-17",是一個日期格式的內容,Elasticsearch會把這個note字段設置成日期格式,但remark字段第二條數據過來的卻是"Comment Submit",這只是一段文本,remark字段已經是日期格式了,第二條保存就會拋出異常。
針對日期檢測,我們可以選擇關閉,如下:
PUT /music {"mappings": {"children": {"date_detection": false}} }但我們針對Long類型,Boolean類型的,同樣有這種情況,逐一關閉可行性不高,為此我們需要使用動態模板配置。
動態模板
使用動態模板(dynamic template),我們可以通過字段名稱或數據類型來應用不同的映射,來定制自己的模板。
例如我們使用字段名稱后綴的方式:
PUT /music {"mappings": {"children": {"dynamic_templates": [{ "en": {"match": "*_en", "match_mapping_type": "string","mapping": {"type": "text","analyzer": "english"}}}]}} }這個含義是如果字段以_en結尾,那么類型為text,analyzer為english,否則類型為string,analyzer為standard。
測試內容:
PUT /music/children/1 {"content": "you are my sunshine" }PUT /music/children/2 {"content_en": "you are my sunshine" }理論上content使用standard分詞器,4個單詞均可被索引到,content_en字段使用english分詞器,are作為停用詞會被移除掉。
對索引進行搜索可知:
GET /music/children/_search {"query": {"match": {"content_en": "are"}} }結果是空
{"took": 1,"timed_out": false,"_shards": {"total": 5,"successful": 5,"skipped": 0,"failed": 0},"hits": {"total": 0,"max_score": null,"hits": []} }使用其他的關鍵詞,或使用content字段,搜索均能出結果,符合預期。
小建議以上只是動態映射模板的一個小案例,真實生產環境中文檔的復雜度遠高于此,對文檔的結構而言,優先手動創建索引,明確每個字段的含義和數據類型,其次再做通用的動態映射模板,但也需要定時檢查索引下的數據類型,以防出現意外情況。
小結
本篇主要介紹索引的相關知識,包含索引的CRUD、自定義分詞器、映射對象的知識,最后簡單介紹了映射模板的配置,實際生產如果有乃至動態模板配置,肯定遠比這個復雜,這里僅作拋磚引玉,謝謝。
專注Java高并發、分布式架構,更多技術干貨分享與心得,請關注公眾號:Java架構社區
總結
以上是生活随笔為你收集整理的elasticsearch 索引_Elasticsearch系列---索引管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python批处理文件_Python文件
- 下一篇: eslint 保存自动格式化_ESLin