Elasticsearch index 設(shè)置 False,為什么還可以被檢索到?
在 Elasticsearch 中,mapping 定義了索引中的字段類型及其處理方式。
近期有球友提問,為什么設(shè)置了 index: false 的字段仍能被檢索。
本文將詳細(xì)探討這個問題,并引入列式存儲的概念,幫助大家更好地理解 Elasticsearch 的存儲和查詢機(jī)制。
1、問題描述
我們創(chuàng)建了一個名為 my-index-000001 的索引,并為其添加了一個名為 employee-id 的字段,該字段的 index 屬性被設(shè)置為 false。
按理說,這個字段不應(yīng)該被索引,也不應(yīng)能被檢索,但在執(zhí)行查詢時,卻能檢索到該字段。這是為什么呢?
PUT /my-index-000001
{
"mappings": {
"properties": {
"employee-id": {
"type": "keyword",
"index": false
}
}
}
}
POST /my-index-000001/_doc/1
{
"employee-id": "1111"
}
POST /my-index-000001/_search
{
"query": {
"term": {
"employee-id": "1111"
}
}
}
問題來源:https://t.zsxq.com/GuwKP
2、原因分析
在 Elasticsearch 中,index 選項控制字段值是否被索引。
默認(rèn)情況下,所有字段都是被索引的 (index: true)。當(dāng) index 設(shè)置為 false 時,字段不會被索引,因此不能通過常規(guī)查詢方法高效地檢索該字段。
https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-index.html
然而,對于某些特定類型的字段,即使設(shè)置了 index: false,它們?nèi)匀豢梢酝ㄟ^ doc_values 進(jìn)行查詢。
這其實就是咱們的問題所在!
這些特定字段類型包括:
- 數(shù)值類型(Numeric types)
- 日期類型(Date types)
- 布爾類型(Boolean type)
- IP 類型(IP type)
- 地理點(diǎn)類型(Geo_point type)
- 關(guān)鍵字類型(Keyword type)
對于這些類型的字段,即使 index 設(shè)置為 false,只要 doc_values 啟用,它們?nèi)匀豢梢员徊樵儭?/p>
查詢效率會較低,因為需要對整個索引進(jìn)行全掃描(full scan)。
3、列式存儲概述
列式存儲(Columnar Storage)是指將每個字段的數(shù)據(jù)獨(dú)立存儲,這種存儲方式不同于傳統(tǒng)的行式存儲。
在數(shù)據(jù)倉庫和大數(shù)據(jù)處理系統(tǒng)中,列式存儲優(yōu)化了讀取和分析操作。
以下是一些常見的列式存儲格式及其應(yīng)用:
- Parquet:廣泛用于 Apache Hadoop 生態(tài)系統(tǒng)中的數(shù)據(jù)處理,提供高效的存儲和壓縮。
- ORC(Optimized Row Columnar):主要用于 Apache Hive 和 Hadoop 生態(tài)系統(tǒng),提供優(yōu)化的列存儲格式。
- Cassandra:分布式數(shù)據(jù)庫系統(tǒng),采用行和列的混合存儲方式,支持列級別的高效查詢。
列式存儲 VS 行式存儲
在 Elasticsearch 中,doc_values 是一種列式存儲機(jī)制,用于存儲字段的數(shù)據(jù),以支持高效的排序和聚合操作。
這里就是明顯區(qū)別于“倒排索引”的一種正排索引技術(shù),詳細(xì)解讀參見《一本書講透 Elasticsearch》P97-P98。
Doc values 是指在文檔索引時創(chuàng)建的存儲在磁盤數(shù)據(jù)結(jié)構(gòu),它們以列式存儲的方式保存與 _source 相同的數(shù)據(jù),從而大大提高了排序和聚合操作的效率。除文本 text 和帶注釋的文本(annotated_text ,新類型)字段外,幾乎所有字段類型都支持 doc values。
https://www.elastic.co/guide/en/elasticsearch/reference/current/doc-values.html
3.1 列式存儲示例:詞組數(shù)據(jù)舉例
假設(shè)我們有以下文檔集合,這些文檔包含多個字段,包括 employee-id 雇員 id 序號和 address 地址信息:
[
{"employee-id": "1111", "name": "Alice", "age": 30, "address": "123 Main St, Springfield, IL"},
{"employee-id": "1112", "name": "Bob", "age": 25, "address": "456 Elm St, Springfield, IL"},
{"employee-id": "1113", "name": "Charlie", "age": 35, "address": "789 Oak St, Springfield, IL"}
]
列式存儲如下圖所示:
圖片
當(dāng)這些文檔被索引到 Elasticsearch 中時,啟用了 doc_values 的字段會以列式存儲的方式獨(dú)立存儲。
假設(shè)我們?yōu)?employee-id、address 字段啟用了 doc_values,其存儲結(jié)構(gòu)如下:
employee-id 列存儲:
"1111"
"1112"
"1113"
address 列存儲:
"123 Main St, Springfield, IL"
"456 Elm St, Springfield, IL"
"789 Oak St, Springfield, IL"
3.2 列式存儲查詢行為
回到開篇問題,在這種情況下,如果我們對 employee-id 進(jìn)行查詢:
POST /my-index/_search
{
"profile": true,
"query": {
"term": {
"employee-id": "1111"
}
}
}
由于 employee-id 字段啟用了 doc_values,但沒有被索引,Elasticsearch 會使用基于 doc_values 的查詢機(jī)制來處理。
這個查詢會遍歷 employee-id 列的數(shù)據(jù),找到匹配 "1111" 的文檔。
這里就分析出了 index:false, 依然可以被檢索的原因。
圖片
再進(jìn)一步驗證,
PUT /my-index-0606
{
"mappings": {
"properties": {
"employee-id": {
"type": "keyword",
"doc_values": true
},
"name": {
"type": "text"
},
"age": {
"type": "integer",
"doc_values": true
},
"address": {
"type": "keyword",
"index":false
}
}
}
}
POST /my-index-0606/_bulk
{ "index": { "_id": "1" } }
{ "employee-id": "1111", "name": "Alice", "age": 30, "address": "123 Main St, Springfield, IL" }
{ "index": { "_id": "2" } }
{ "employee-id": "1112", "name": "Bob", "age": 25, "address": "456 Elm St, Springfield, IL" }
{ "index": { "_id": "3" } }
{ "employee-id": "1113", "name": "Charlie", "age": 35, "address": "789 Oak St, Springfield, IL" }
POST my-index-0606/_search
{
"query": {
"term": {
"address": "123 Main St, Springfield, IL"
}
}
}
得到結(jié)果如下:
圖片
這就是基于正排索引做的輪詢的結(jié)果。
3.3 列式存儲的優(yōu)勢和劣勢
- 優(yōu)勢:
列式存儲使得對特定字段的聚合和排序操作更加高效,因為只需要讀取相關(guān)列的數(shù)據(jù),而不是整個文檔的所有字段。
舉例說明,假設(shè)我們有一個包含員工信息的索引(在之前基礎(chǔ)上新增了字段),文檔結(jié)構(gòu)如下:
[
{"employee-id": "1111", "name": "Alice", "age": 30, "salary": 5000, "address": "123 Main St, Springfield, IL"},
{"employee-id": "1112", "name": "Bob", "age": 25, "salary": 6000, "address": "456 Elm St, Springfield, IL"},
{"employee-id": "1113", "name": "Charlie", "age": 35, "salary": 7000, "address": "789 Oak St, Springfield, IL"}
]
如果行式存儲:讀取每個文檔時,所有字段數(shù)據(jù)都被加載,即使我們只關(guān)心其中一個字段的數(shù)據(jù)。
行式存儲舉例——計算平均薪資時,整個文檔(包括 name、age、address 等)都要被讀取。如下圖所示:
圖片
讀取整行信息,有點(diǎn)類似 MySQL 如下操作:
SELECT * FROM employees WHERE employee-id = '1111';
返回結(jié)果:
{"employee-id": "1111", "name": "Alice", "age": 30, "salary": 5000, "address": "123 Main St, Springfield, IL"}
如果列式存儲:只讀取特定字段的數(shù)據(jù)。
列式存儲舉例——計算平均薪資時,只需讀取 salary 列的數(shù)據(jù)即可,避免了讀取無關(guān)字段的數(shù)據(jù)。如下圖所示。
圖片
列式存儲讀取一列數(shù)據(jù),有點(diǎn)類似 MySQL如下操作:
SELECT age FROM employees;
返回結(jié)果:
[30, 25, 35]
- 劣勢:對于未被索引的字段,查詢效率較低,因為需要遍歷整個列的數(shù)據(jù)來匹配查詢條件。
4、結(jié)論
通過這些示例,我們可以更清楚地理解 Elasticsearch 中列式存儲和 doc_values 的應(yīng)用。
列式存儲使得對特定字段的聚合和排序操作更加高效,但對于未被索引的字段,查詢效率較低,因為需要遍歷整個列的數(shù)據(jù)來匹配查詢條件。
希望這些解釋能幫助你更好地理解 Elasticsearch 的存儲和查詢機(jī)制。
如果你對字段的查詢和聚合有特定需求,合理使用 index 和 doc_values 設(shè)置可以大大提升性能和效率。