一、真實面試問題
死磕 Elasticsearch 技術羣好友留言:面試
二、面試官在問 Elasticsearch range 過濾時,他心裏在想什麼?
最基礎的:看你了不瞭解 range 查詢?算法
其次:看你了不瞭解 range 查詢 支持哪些數據類型?sql
再次:看你了不瞭解對應數據類型底層存儲數據結構或算法?數組
最後:看你了不瞭解對應數據結構的 range 查詢原理?數據結構
最根本的,想看你的底層原理扎不紮實?app
咱們在被面試官面試的時候,實際上也是咱們本身面試「面試官」。elasticsearch
站在面試官的角度考慮問題,多問幾個問什麼?基本就能揣摩出他心裏的想法。ide
至少,能問這個問題,面試官不是簡單的 CRUD 業務面試官,仍是看重底層原理的,側面印證了面試官所在的團隊的技術有必定技術含量。post
三、Elasticsearch range 過濾 問題拆解
3.1 range 查詢 介紹
腦海裏想一下:本身用過的 range 查詢。性能
查詢給定時間段的數據(date);
查詢給定區間範圍的數值類型數據(integer,long等);
查詢給定區間範圍的字符串類型數據(text/keyword)。
由於是覆盤總結講解,我會列個Demo解讀一下:
DELETE my_index_0002
PUT my_index_0002
{
"mappings": {
"properties": {
"post_date": {
"type": "date"
},
"user": {
"type": "keyword"
},
"name": {
"type": "text"
},
"age": {
"type": "integer"
}
}
}
}
PUT my_index_0002/_bulk
{"index":{"_id":1}}
{"post_date":"2019-08-05T15:03:02", "user":"lucy", "name":"lucy", "age":18}
{"index":{"_id":2}}
{"post_date":"2017-03-02T01:08:35", "user":"haimeimei", "name":"hanmeimei", "age":25}
{"index":{"_id":3}}
{"post_date":"2020-08-05T15:03:02", "user":"hank", "name":"hank", "age":7}
POST my_index_0002/_search
{"profile": "true",
"query": {
"range": {
"post_date": {
"gte": "2017-01-01T00:00:00",
"lte": "2019-12-31T23:59:59"
}
}
}
}
POST my_index_0002/_search
{ "profile": "true",
"query": {
"range": {
"user": {
"gte": "aaaa",
"lte": "zzzz"
}
}
}
}
POST my_index_0002/_search
{
"profile": "true",
"query": {
"range": {
"age": {
"gte": 1,
"lte": 20
}
}
}
}
POST my_index_0002/_search
{
"profile": "true",
"query": {
"range": {
"name": {
"gte": "aaaa",
"lte": "zzzz"
}
}
}
}
實戰中用過 Elasticsearch 的,都會用過上面的 range 查詢。
但,面試官想聽到的不是這些初級內容。
3.2 range 查詢支持的數據類型
如上舉例已經列舉了。
數值類型 舉例:integer、long 等
日期類型 舉例:date 等
字符串類型 舉例:keyword、 text 等
3.3 range 查詢對應數據類型底層存儲數據結構
以最典型的兩種類型拆解。
3.3.1 數值類型對應數據結構—— Block K-d Tree
Wood 大叔的相關文章有詳細解讀:
Elasticsearch 從5.0開始引入Block K-d Tree這種新數值類型索引數據結構,再也不採用倒排索引,該結構更適合範圍查找。
Block K-d Tree 基本思想:
將一個N維的數值空間,不斷選定包含值最多的維度作2分切割,反覆迭代,直到切分出來的空間單元(cell)包含的值數量小於某個數值。
對於單維度的數據,實際上就是簡單的對全部值作一個排序,而後反覆從中間作切分,生成一個相似於 B-tree 這樣的結構。
和傳統的 B-tree 不一樣的是,他的葉子結點存儲的不是單值,而是一組值的集合,也就是是所謂的一個 Block 。每一個 Block 內部包含的值數量控制在512- 1024個,保證值的數量在block之間儘可能均勻分佈。
https://elasticsearch.cn/article/446
其數據結構以下圖所示(大體):
圖片來源:wood 大叔
3.3.2 字符串類型 text 對應數據結構——LSM 樹
Elasticsearch,HBase, Cassandra, RockDB 等都是基於 LSM Tree 來構建索引。
LSM tree 是一種分層、有序、面向磁盤的數據結構,其核心思想是其充分的利用了磁盤批量的順序寫要遠比隨機寫性能高出不少的特色。
LSM tree的核心特色:
第一:將索引分爲內存和磁盤兩部分,並在內存達到閾值時啓動樹合併(Merge Trees);
第二:用批量寫入代替隨機寫入,而且用預寫日誌 WAL 技術(Elasticsearch 中爲 translog 事務日誌)保證內存數據,在系統崩潰後能夠被恢復;
第三:數據採起相似日誌追加寫的方式寫入(Log Structured)磁盤,以順序寫的方式提升寫入效率。
3.4 range 原理
針對 3.1 demo 中的幾種 range 查詢,加 profile: true 能看到底層邏輯:
整形 age 以及 日期類型 post_date 的 range 查詢 後臺使用的 Lucene 的 IndexOrDocValuesQuery 。
post_date date 類型實際會轉換成時間戳:
"description" : "post_date:[1483228800000 TO 1577836799999]",
Elasticsearch 5.4 版本新增的 indexOrDocValuesQuery 將 Range 查詢過程當中的順序訪問任務扔給 Block k-d Tree 索引,將隨機訪任務交給 doc values 。
字符串類型 user 及 name 的 range 查詢 後臺使用的是 Lucene 的 MultiTermQueryConstantScoreWrapper。
MultiTermQueryConstantScoreWrapper 本質是:多個 term query 的組合。
3.4.1 數值類型的 range 原理
Lucene 將單維度數據反覆從中間作切分後, B-tree的非葉子結點部分放在內存裏,而葉子結點牢牢相鄰存放在磁盤上。
當作 range 查詢的時候,內存裏的 B-tree 能夠幫助快速定位到知足查詢條件的葉子結點塊在磁盤上的位置,以後對葉子結點塊的讀取幾乎都是順序的。
上面這句話其實就是面試官最想聽到的。
這個就和 B+ 樹(Mysql 索引數據結構)很相似了,B+樹的索引和數據分離的機制使得其適合作範圍查詢。
若是要基於 B+ 樹作 Range 查詢,舉例查詢(a,b)之間的全部元素, 實際操做以下:
步驟1:經過二分查找找到 a 元素;
步驟2:依次向後遍歷葉子節點對應的鏈表元素,直到數值 > b 中止。
這樣就能獲得(a, b) 之間的全部元素值。
3.4.2 字符串類型text 的range原理
針對 LSM-Tree ,核心的數據結構就是 SSTable ,全稱是 Sorted String Table ,SSTable 的概念其實也是來自於 Google 的 Bigtable 論文。
SSTable 是一種擁有持久化,有序且不可變的的鍵值存儲結構,它的 key 和 value 都是任意的字節數組,而且了提供了按指定key查找和指定範圍的key區間迭代遍歷的功能。
SSTable 內部包含了一系列可配置大小的 Block 塊,典型的大小是 64 KB,關於這些Block塊的索引存儲在 SSTable 的尾部,用於幫助快速查找特定的 Block。
查詢的原理:當一個SSTable被打開的時候,索引會被加載到內存,而後根據key在內存索引裏面進行一個二分查找,查到該 key 對應的磁盤的 offset 以後,而後去磁盤把響應的塊數據讀取出來。
range 的原理:在查詢基礎上,基於 SStable 文件(已有序)向後遍歷找到直到找到大於右區間值中止遍歷。
固然若是內存足夠大的話,能夠直接把 SSTable 直接經過 MMap 的技術映射到內存中,從而提供更快的查找。
四、小結
小問題,大道理。「底層的技術永遠不過期「。
涉及到算法的底層邏輯,任何一個知識點的背後都是行業的頂級期刊的頂級論文,要了解更多細節須要查看更多資料。
本文未深究算法細節,不免會有紕漏,歡迎你們指正交流反饋。
和你一塊兒,死磕Elastic!