索引操作
創建索引
es創建索引的請求方式如下:
PUT /<index>
- 請求的方法用PUT。
- /后面直接跟索引的名稱即可。
- 索引的設置和字段都放在Body中。
比如我們創建一個名字叫組織機構的索引,這個索引只有兩個字段,一個id,一個name。並且這個索引設置為2個分片,2個副本。
我們使用Postman發送請求,如下:
http://localhost:9200/orgnization
請求的方法選擇PUT。然后在請求體(Body)中,寫上索引的字段名稱,索引的分片數和副本數,如下:
{
"settings":{
"number_of_shards":2,
"number_of_replicas":2
},
"mappings":{
"properties":{
"id":{
"type":"long"
},
"name":{
"type":"text"
}
}
}
}
我們觀察一下,請求體中分為兩個部分:settings和mappings。在settings中,我們設置了分片數和副本數。
- number_of_shards:分片的數量;
- number_of_replicas:副本的數量;
在mappings中,我們設置索引的字段,在這里,我們只設置了id和name,id的映射類型是long,name的映射類型是text。
請求體寫完后,我們點擊發送,es返回的結果如下:
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "orgnization"
}
說明索引創建成功,索引的名字正是我們在請求中設置的orgnization。
然后,我們通過elasticsearch-head插件觀察一下剛才創建的索引
我們可以看到索引orgnization已經創建好了,它有2個分片,分別是0和1,並且每個分片都是兩個副本。如果我們仔細觀察這個圖,可以看出node-chun節點中的0分片,和node-yao節點中的1分片,它們的邊框是加粗的,這說明它們是主節點,而邊框沒有加粗的節點是從節點,也就是我們說的副本節點。
查看索引
如果我們要查看一個索引的設置,可以通過如下請求方式:
GET /<index>
在我們的例子中,查看orgnization索引的設置,我們在Postman中發送如下的請求:
{
"orgnization": {
"aliases": {},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text"
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "2",
"provided_name": "orgnization",
"creation_date": "1636182188268",
"number_of_replicas": "2",
"uuid": "93U8-D3FSf2ZsVveV6uPUw",
"version": {
"created": "7100299"
}
}
}
}
}
我們可以看到索引的具體設置,比如:mapping的設置,分片和副本的設置。這些和我們創建索引時候的設置是一樣的。
修改索引
索引一旦創建,我們是無法修改里邊的內容的,比如說修改索引字段的名稱。但是我們是可以向索引中添加其他字段的,添加字段的方式如下:
PUT /<index>/_mapping
然后在我們的請求體中,寫好新添加的字段。比如,在我們的例子當中,新添加一個type字段,它的類型我們定義為long,請求如下:
http://localhost:9200/orgnization/_mapping
請求類型要改為PUT,請求體如下:
{
"properties": {
"type": {
"type": "long"
}
}
}
添加索引字段成功,我們再使用GET查看一下索引,如圖:
{
"orgnization": {
"aliases": {},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text"
},
"type": {
"type": "long"
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "2",
"provided_name": "orgnization",
"creation_date": "1636182188268",
"number_of_replicas": "2",
"uuid": "93U8-D3FSf2ZsVveV6uPUw",
"version": {
"created": "7100299"
}
}
}
}
}
我們可以成功的查詢到新添加的索引字段了。
刪除索引
如果我們要刪除一個索引,請求方式如下:
DELETE /<index>
假如我們要刪除剛才創建的orgnization索引,我們只要把請求的方法改成DELETE,然后訪問我們索引就可以
http://localhost:9200/orgnization
關閉索引
如果索引被關閉,那么關於這個索引的所有讀寫操作都會被阻斷。索引的關閉也很簡單,請求方式如下:
POST /<index>/_close
在我們的例子中,如果要關閉索引,將請求方法改成POST,然后發送如下請求:
http://localhost:9200/orgnization/_close
打開索引
與關閉索引相對應的是打開索引,請求方式如下:
POST /<index>/_open
在我們的例子中,如果要打開索引,將請求方法改成POST,然后發送如下請求:
http://localhost:9200/orgnization/_open
凍結索引
凍結索引和關閉索引類似,關閉索引是既不能讀,也不能寫。而凍結索引是可以讀,但是不能寫。凍結索引的請求方式如下:
POST /<index>/_freeze
對應我們的例子當中:
http://localhost:9200/orgnization/_freeze
解凍索引
與凍結索引對應的是解凍索引,方式如下:
POST /<index>/_unfreeze
對應我們的例子:
http://localhost:9200/orgnization/_unfreeze
Setting配置
setting為ES索引的配置屬性,索引的配置項按是否可以更改分靜態(static)屬性與動態配置。
靜態配置即索引創建后不能修改,靜態配置只能在創建索引時或者在狀態為 closed index的索引(閉合的索引)上設置。
索引靜態配置
index.number_of_shards 主分片數,默認為5。只能在創建索引時設置,不能修改。
index.shard.check_on_startup 是否在索引打開前檢查分片是否損壞,當檢查到分片損壞將禁止分片被打開。可選值:false:不檢測;checksum:只檢查物理結構;true:檢查物理和邏輯損壞,相對比較耗CPU;fix:類同與false,7.0版本后將廢棄。默認值:false。
index.codec 數據存儲的壓縮算法,默認算法為LZ4,也可以設置成best_compression,best_compression壓縮比較好,但存儲性能比LZ4差。
index.routing_partition_size 路由分區數,默認為 1,只能在索引創建時設置。此值必須小於index.number_of_shards,如果設置了該參數,如果設置了該參數,其路由算法為: (hash(_routing) + hash(_id) % index.routing_parttion_size ) % number_of_shards。如果該值不設置,則路由算法為 hash(_routing) % number_of_shardings,_routing默認值為_id。
索引動態配置
index.refresh_interval 執行刷新操作的頻率,這使得索引的最近更改可以被搜索。默認為 1s。可以設置為 -1 以禁用刷新。
index.max_result_window 用於索引搜索的 from+size 的最大值。默認為 10000。
index.max_docvalue_fields_search 一次查詢最多包含開啟doc_values字段的個數,默認為100。
index.number_of_replicas 每個主分片的副本數,默認為 1,該值必須大於等於0
index.auto_expand_replicas 基於可用節點的數量自動分配副本數量,默認為 false(即禁用此功能)
index.blocks.read_only 設置為 true 使索引和索引元數據為只讀,false 為允許寫入和元數據更改。
index.max_rescore_window 在搜索此索引中 rescore 的 window_size 最大值
index.blocks.read_only_allow_delete 與index.blocks.read_only基本類似,唯一的區別是允許刪除動作。
index.blocks.write 設置為 true 可禁用對索引的寫入操作。
index.blocks.read 設置為 true 可禁用對索引的讀取操作
index.blocks.metadata 設置為 true 可禁用索引元數據的讀取和寫入。
index.max_refresh_listeners 索引的每個分片上可用的最大刷新偵聽器數。
對於已存在的索引,我們想要修改它的動態配置,可以使用_settings方法。
PUT /test_setting/_settings
{
"number_of_replicas": "0"
}
mapping配置
通常索引的 Mapping 結構可以在創建索引時由 ElasticSearch 幫我們自動構建,字段類型由 ElasticSearch 自動推斷,但這樣做有一些問題,比如字段類型推斷不准確,默認所有字段都會構建倒排索引等,自定義Mapping就可以解決上述這些問題。
我們創建了索引,在創建索引的時候,我們指定了mapping屬性,mapping屬性中規定索引中有哪些字段,字段的類型是什么。在mapping中,我們可以定義如下內容:
- 類型為String的字段,將會被全文索引;
- 其他的字段類型包括:數字、日期和geo(地理坐標);
- 日期類型的格式;
- 動態添加字段的映射規則;
字段的可用類型如下:
- 簡單的類型,比如:text(分詞),keyword(不分詞),date,long,double,boolean,ip。我們可以看到,類型當中沒有String,字符串的類型是text,所有text類型的字段都會被全文索引。數字類型有兩個,long(長整型)和double(浮點型)。
- JSON的層級類型:Object(對象)和Nested(數組對象)。Object類型時,該字段可以存儲一個JSON對象;Nested類型時,該字段可以存儲一個數組對象。
- 復雜的類型:包括 geo_point、geo_shape和completion。
索引的創建需要配置mapping與setting兩部分。基本格式:
{
"mappings":{
"_all":{
"enabled":false #默認情況,ElasticSarch自動使用_all所有的文檔的域都會被加到_all中進行索引。可以使用"_all" : {"enabled":false} 開關禁用它。如果某個域不希望被加到_all中,可以使用"include_in_all":false關閉
},
"properties":{
"uuid":{
"type":"text",
"copy_to":"_search_all", #對應_search_all字段,可以對其進行全文檢索
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":150 #ignore_above 默認值是256,當字段文本的長度大於指定值時,不做倒排索引。
}
}
},
"name":{
"type":"text",
"copy_to":"_search_all",
"analyzer":"ik_max_word", # ik_max_word 插件會最細粒度分詞
"search_analyzer":"ik_smart", # ik_smart 粗粒度分詞
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":150
}
}
},
"dt_from_explode_time":{
"type":"date",
"copy_to":"_search_all",
"format":"strict_date_optional_time||epoch_millis"
},
"_search_all":{
"type":"text"
}
},
"date_detection":false, #關閉日期自動檢測,如果開啟,會對於設置為日期格式的字段進行判斷
"dynamic_templates":[ #用於自定義在動態添加field的時候自動給field設置的數據類型
{
"strings":{
"match_mapping_type":"string",
"mapping":{
"type":"text",
"copy_to":"_search_all",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":150
}
}
}
}
}
]
},
"settings":{
"index":{
"number_of_shards":6, #分片數量
"number_of_replicas":1 #副本數量
}
}
}
常用數據類型
text、keyword、number、array、range、boolean、date、geo_point、ip、nested、object。
- text:默認會進行分詞,支持模糊查詢(5.x之后版本string類型已廢棄,請大家使用text)。
- keyword:不進行分詞,默認開啟doc_values來加速聚合排序操作,占用了大量磁盤io 如非必須可以禁用doc_values。
- number:如果只有過濾場景 用不到range查詢的話,使用keyword性能更佳,另外數字類型的doc_values比字符串更容易壓縮。
- range:對數據的范圍進行索引,目前支持 number range、date range 、ip range。
- array:es不需要顯示定義數組類型,只需要在插入數據時用’[]‘表示即可。[]中的元素類型需保持一致。
- boolean: 只接受true、false,也可以是字符串類型的“true”、“false”。
- date:支持毫秒、根據指定的format解析對應的日期格式,內部以long類型存儲。
- geo_point:存儲經緯度數據對。
- ip:將ip數據存儲在這種數據類型中,方便后期對ip字段的模糊與范圍查詢。
- ested:嵌套類型,一種特殊的object類型,存儲object數組,可檢索內部子項。
- object:嵌套類型,不支持數組。
{
"mappings": {
"properties": {
"price": {
"type": "long"
},
"color": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"sold_date": {
"type": "date", // 使用date類型時要指定format,否則在使用range查詢+時間表達式now/d等時,會特別坑爹
"format":"yyyyMMdd"
}
}
}
}
其他參數:
"field": {
"type": "text", //文本類型 ,指定類型
"index": "false"// ,設置成false,字段將不會被索引
"analyzer":"ik"//指定分詞器
"boost":1.23//字段級別的分數加權
"doc_values":false//對not_analyzed字段,默認都是開啟,analyzed字段不能使用,對排序和聚合能提升較大性能,節約內存,如果您確定不需要對字段進行排序或聚合,或者從script訪問字段值,則可以禁用doc值以節省磁盤空間:
"fielddata":{"loading" : "eager" }//Elasticsearch 加載內存 fielddata 的默認行為是 延遲 加載 。 當 Elasticsearch 第一次查詢某個字段時,它將會完整加載這個字段所有 Segment 中的倒排索引到內存中,以便於以后的查詢能夠獲取更好的性能。
"fields":{"keyword": {"type": "keyword","ignore_above": 256}} //可以對一個字段提供多種索引模式,同一個字段的值,一個分詞,一個不分詞
"ignore_above":100 //超過100個字符的文本,將會被忽略,不被索引
"include_in_all":ture//設置是否此字段包含在_all字段中,默認是true,除非index設置成no選項
"index_options":"docs"//4個可選參數docs(索引文檔號) ,freqs(文檔號+詞頻),positions(文檔號+詞頻+位置,通常用來距離查詢),offsets(文檔號+詞頻+位置+偏移量,通常被使用在高亮字段)分詞字段默認是position,其他的默認是docs
"norms":{"enable":true,"loading":"lazy"}//分詞字段默認配置,不分詞字段:默認{"enable":false},存儲長度因子和索引時boost,建議對需要參與評分字段使用 ,會額外增加內存消耗量
"null_value":"NULL"//設置一些缺失字段的初始化值,只有string可以使用,分詞字段的null值也會被分詞
"position_increament_gap":0//影響距離查詢或近似查詢,可以設置在多值字段的數據上火分詞字段上,查詢時可指定slop間隔,默認值是100
"store":false//是否單獨設置此字段的是否存儲而從_source字段中分離,默認是false,只能搜索,不能獲取值
"search_analyzer":"ik"//設置搜索時的分詞器,默認跟ananlyzer是一致的,比如index時用standard+ngram,搜索時用standard用來完成自動提示功能
"similarity":"BM25"//默認是TF/IDF算法,指定一個字段評分策略,僅僅對字符串型和分詞類型有效
"term_vector":"no"//默認不存儲向量信息,支持參數yes(term存儲),with_positions(term+位置),with_offsets(term+偏移量),with_positions_offsets(term+位置+偏移量) 對快速高亮fast vector highlighter能提升性能,但開啟又會加大索引體積,不適合大數據量用
}
常用的有:
- type:field的類型。
- index:該field是否會被索引。
- analyzer:指定的分詞器。
- doc_values:開啟后對聚合入分組或排序有增益,默認開啟。
mapping之date date_nanos
JSON沒有date數據類型,但我們可以把以下類型的數據作為日期時間存入ES。
在ES的內部,這些數據都是按照毫秒數(長整型)存儲的,只是它們展現形式有如下多種。
類型 | 說明 |
---|---|
字符串 | 日期格式的字符串,如"2015-01-01"或"2015/01/01 12:10:30" |
長整型 | 從開始紀元(1970-01-01 00:00:00 UTC)開始的毫秒數 |
整型 | 從開始紀元(1970-01-01 00:00:00 UTC)開始的秒數 |
上面的UTC(Universal Time Coordinated) 叫做世界統一時間,中國大陸和 UTC 的時差是 + 8 ,也就是 UTC+8。在ES內部,時間以毫秒數的UTC存儲。
1、日期格式
date的格式可以被指定的,如果沒有特殊指定,默認格式是"strict_date_optional_time||epoch_millis"
這段話可以理解為格式為strict_date_optional_time或者epoch_millis
(1)什么是epoch_millis?
epoch_millis就是從開始紀元(1970-01-01 00:00:00 UTC)開始的毫秒數-長整型。
如下圖,2020/8/31 14:57:56是我們常用的日期格式,它距離1970-01-01 00:00:00 有 1598857076000豪秒。所以可以用長整型1598857076000表示2020/8/31 14:57:56。
(2)什么是strict_date_optional_time?
strict_date_optional_time是date_optional_time的嚴格級別,這個嚴格指的是年份、月份、天必須分別以4位、2位、2位表示,不足兩位的話第一位需用0補齊。
常見的格式有如下:
- yyyy
- yyyyMM
- yyyyMMdd
- yyyyMMddHHmmss
- yyyy-MM
- yyyy-MM-dd
- yyyy-MM-ddTHH:mm:ss
- yyyy-MM-ddTHH:mm:ss.SSS
- yyyy-MM-ddTHH:mm:ss.SSSZ
工作常見到是"yyyy-MM-dd HH:mm:ss",但是ES是不支持這格式的,需要在dd后面加個T,這個是固定格式。上面最后一個里大寫的"Z"表示時區。
你就是想用yyyy-MM-dd HH:mm:ss?
date類型,還支持一個參數format,它讓我們可以自己定制化日期格式。
比如format配置了“格式A||格式B||格式C”,插入一個值后,會從左往右匹配,直到有一個格式匹配上。
{
"mappings":{
"properties":{
"birthday":{
"type":"date",
"format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
(3)date_nanos類型,支持納秒
date類型支持到毫秒,如果特殊情況下用到納秒得用date_nanos這個類型。
為索引自定義Mapping
語法結構:
{
"mappings": {
"properties": {
"age": {
"type": "integer"
},
"email": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}
PUT http://localhost:9200/users。請求的方法我們要使用PUT,路徑是我們的索引名稱,請求體當中是我們為索引添加的字段和字段的類型。
最佳實踐
我們可以完全參考手冊編寫 Mapping ,但不建議這樣做,容易出錯,調試也麻煩,可以通過創建一個測試索引並插入測試文檔,然后查看ElasticSearch為這個索引自動創建的Mapping,基於這個Mapping進行修改來構建我們自己的 Mapping。
在存在的映射中添加字段
正如上面所示,我們在一個索引中添加了字段,但是現在我們要補充額外的字段,這時,我們要怎么做呢?
PUT /users/_mapping
{
"properties": {
"employee-id": {
"type": "keyword",
"index": false
}
}
}
我們使用PUT方法,后面跟隨我們的索引名稱,再接上_mapping,請求體中是我們新添加的映射字段,我們指定了字段的類型為keyword,index索引為false,說明這個字段只用於存儲,不會用於搜索,搜索這個字段是搜索不到的。
我們在更新字段時候,是不能修改字段的類型的。如果我們要修改字段的類型,最好是新建一個新的字段,指定正確的類型,然后再更新索引,以后我們只需要查詢這個新增的字段就可以了。
查看索引中的字段映射
如果我們要查看已知索引的字段映射,可以向ES發送如下的請求:
GET /users/_mapping
請求的方法是GET,請求的路徑是我們索引的名稱my-index,再加上一個_mapping,得到的返回結果如下:
返回的結果中,我們可以看到索引的名稱users,還有我們添加的字段,也包括后續補充的employee-id字段。
修改mapping中的字段類型
mapping中字段類型一旦設定后禁止直接修改。因為lucene實現的倒排索引生成后不允許修改,除非重建索引映射,然后做reindex操作。
以上面例子的索引orgnization為例。
(1)創建一個備份索引orgnization_bak
{
"settings":{
"number_of_shards":2,
"number_of_replicas":2
},
"mappings":{
"properties":{
"id":{
"type":"long"
},
"name":{
"type":"text"
}
}
}
}
(2) reindex操作
POST http://localhost:9200/_reindex
請求體:
{
"source": {"index":"舊索引"},
"dest": {"index":"備份索引"}
}
其中舊索引就是orgnization,備份索引是orgnization_bak。
(3)刪除舊索引orgnization
(4)創建新索引orgnization,重新指定字段類型
{
"settings":{
"number_of_shards":2,
"number_of_replicas":2
},
"mappings":{
"properties":{
"id":{
"type":"long"
},
"name":{
"type":"keyword"
}
}
}
}
(5)再reindex操作
POST http://localhost:9200/_reindex
請求體為:
{
"source": {"index":"備份索引"},
"dest": {"index":"新索引"}
}
此時的備份索引是orgnization_bak,新索引就是我們剛剛創建的orgnization。
注:在大數據量的情況,reindex操作不知道效率如何,慎用。