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