今天面試問到一個 Elasticsearch 問題,給我問懵逼了......

1、真實面試問題

死磕 Elasticsearch 技術羣好友留言:

2、面試官在問 Elasticsearch range 過濾時,他內心在想什麼?

最基礎的:看你了不瞭解 range 查詢?

其次:看你了不瞭解 range 查詢 支持哪些數據類型?

再次:看你了不瞭解對應數據類型底層存儲數據結構或算法?

最後:看你了不瞭解對應數據結構的 range 查詢原理?

最根本的,想看你的底層原理扎不紮實?

我們在被面試官面試的時候,實際上也是我們自己面試“面試官”。

站在面試官的角度考慮問題,多問幾個問什麼?基本就能揣摩出他內心的想法。

至少,能問這個問題,面試官不是簡單的 CRUD 業務面試官,還是看重底層原理的,側面印證了面試官所在的團隊的技術有一定技術含量。

3、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 的技術映射到內存中,從而提供更快的查找。

4、小結

小問題,大道理。“底層的技術永遠不過時“。

涉及到算法的底層邏輯,任何一個知識點的背後都是行業的頂級期刊的頂級論文,要了解更多細節需要查看更多資料。

本文未深究算法細節,難免會有紕漏,歡迎大家指正交流反饋。

和你一起,死磕Elastic!


參考:https://users.cs.duke.edu/~pankaj/publications/papers/bkd-sstd.pdf 

https://elasticsearch.cn/article/446 

https://blog.yugabyte.com/a-busy-developers-guide-to-database-storage-engines-the-basics/

https://cloud.tencent.com/developer/article/1441835 

《搜索技術核心 20 講》


更多推薦:

全網首個十萬+ 閱讀的 Elasticsearch 專欄誕生!


加入中國獲得 Elastic 官方認證工程師人數最多(> 11 人)的圈子!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章