ElasticSearch 2 (10) - 在ElasticSearch之下(深入理解Shard和Lucene Index)
摘要
從底層介紹ElasticSearch Shard的內部原理,以及回答爲什麼使用ElasticSearch有必要了解Lucene的內部工作方式?
-
瞭解ElasticSearch API的代價
- 構建快速的搜索應用
- 不要任何時候都commit
- 何時使用Stored Fields和Document Values
- Lucene可能不是一個合適的工具
-
瞭解索引的存儲方式
- term vector是索引大小的1/2
- 我移除了20%的文件,但是索引佔用空間並未發生任何變化
版本
elasticsearch版本: elasticsearch-2.2.0
內容
索引
毫不誇張的說,如果不瞭解Lucene索引的工作方式,可以說完全不瞭解Lucene,對於ElasticSearch更是如此。
-
可以使搜索更快速
- 可以冗餘信息
- 根據查詢(queries)建立索引
-
在更新速度與查詢速度間妥協
需要注意的是搜索的應用場景
- Grep vs. 全文檢索(full-text indexing)
- Prefix queries vs. edge n-grams
- Phrase queries vs shingles
如果是進行前綴查詢(右模糊匹配)或者是短語查詢(phrase queries),ElasticSearch可能不合適,需要做特殊的優化。(在2.x中,ES對以上應用場景都有支持,具體使用方式可以參考:Search in Depth)
-
Lucene索引的速度
創建索引
以兩個簡單的文件爲例:Lucene in action和Databases。
假設Lucene in action裏有單詞
{index, term, data, Lucene}
Databases裏有單詞
{sql, index, data}
-
樹形結構(Tree structure)
對於range query有序
查詢的時間複雜度爲O(log(n))一般的關係型數據庫大致結構可能是上面這樣的一顆B、B+樹,但是Lucene是另外一種存儲結構。
-
倒排索引(或反向索引Inverted Index)
對於Lucene來說,其主要的存儲結構是一個反向索引,它是一個數組,數組裏面是一個有序的數據字典。
這樣一個存儲結構存在與Lucene的Segment裏。
- term ordinal —— 是一個詞的序號
- term dict —— 是詞的內容
- postings list —— 存放包含詞的文件的id序列
- doc id —— 是每個文件的唯一標識
- document —— 存放每個文件的內容
這兩種結構的一個重要區別是:在增加或刪除文件時,系統會樹形結構頻繁操作,這個結構是一直變化的,而反向索引可以維持不變(Immutable)。????
-
插入?
-
插入 即 創建一個新的segment
-
當有很多segment時,系統會合並segment
這個過程本質上是一個merge sort,做的事情就是- 連接文件
- 合併字典
- 合併postings lists
-
-
刪除?
- 刪除要做的只是置一個標誌位
- 搜索及merge的時候系統會忽略被刪除的文件
- 當有很多刪除發生時,系統會自動運行merge
- 被標記爲已刪除的文件會在merge完成後回收其所佔用的存儲空間
-
孰優孰劣?
-
當更新一個文件的時候,我們實際上是創建了一個新的segment,因此
- 單個文件的更新代價高昂,我們需要使用bulk更新
- 所有的寫操作都是順序執行的
-
Segments永遠不會被修改
- 文件系統緩存友好
- 不會出現鎖的問題
-
Terms 高度去重
- 節省大量高頻詞所佔用的空間
-
文件本身由唯一序號標識
- 跨API通信的時候非常方便
- Lucene可以在單個Query下使用多個索引index
-
Terms 由唯一序號標識
- 對於排序非常重要,只需要比較數字,而非字符串
- 對於faceting(分面搜索)非常重要
-
-
Lucene Index的強大之處(Index intersection)???
很多數據庫不支持同時使用多個索引,但是Lucene支持
-
Lucene爲postings lists 維護一個skip list(Wiki),如果要搜索如上例子中的“red shoe”,系統參考skip list裏的信息可以跳躍檢索(“leap-frog”)
-
對於很多數據庫,它們會挑選最主要的索引(most selective),而忽略其他
關於詳細的index intersection算法以及如何使用skip list的可以參照(nlp.standford.edu)
-
更多索引
-
術語向量(Term vectors)
- 爲每個文件都會創建一個反向索引(Inverted Index)
- 適用場景:搜索更相似的內容
- 也可以用作高亮搜索結果
-
文件值(Document Values)???
- 以文件字段爲單位進行列式存儲
- 適用場景:排序、權重記分
-
有序(集合)文件值???
-
文件有序、字段有序
- 單字段:排序
- 多字段:分面搜索
-
-
分面搜索(Faceting)???
分面是指事物的多維度屬性。例如一本書包含主題、作者、年代等分面。而分面搜索是指通過事物的這些屬性不斷篩選、過濾搜索結果的方法。可以將分面搜索看成搜索和瀏覽的結合。分面搜索作爲一種有效的搜索方式,已經被用在電子商務、音樂、旅遊等多個方面。
例如,谷歌音樂的挑歌頁面,將歌曲分爲節奏、聲調、音色、年代、流派等分面
-
根據文件與搜索匹配的情況計數
- 例如,電商網站根據衣服的款式、衣長、尺碼、顏色等分面。
-
簡單(naive)方案
- 利用哈希表計數(value to count)
- O(#docs) ordinal 查找
- O(#doc) value 查找
-
Lucene方案
- 哈希表(ord to count)
- 最後統計值
- O(#docs) ordinal 查找
- O(#values) value 查找
因爲ordinal是密集的,所以可以簡單用數組array來表示。
-
-
如何使用API?
ElasticSearch高級API 都是基於Lucene API構建的,這些基礎的API包括:
----------------------------------------------------------------------------------------------- API | 用途 | 方法 ----------------------------------------------------------------------------------------------- Inverted index | Term -> doc ids, positions, offsets | AtomicReader.fields ----------------------------------------------------------------------------------------------- Stored fields | Summaries of search results | IndexReader.document ----------------------------------------------------------------------------------------------- Live docs | Ignoring deleted docs | AtomicReader.liveDocs ----------------------------------------------------------------------------------------------- Term vectors | More like this | IndexReader.termVectors ----------------------------------------------------------------------------------------------- Doc values/Norms | Sorting/faceting/scoring | AtomicReader.get*Values -----------------------------------------------------------------------------------------------
-
小結
-
數據有四份重複,只是結構各不相同
- 絕不是浪費空間
- 感謝immutable使數據易於管理
-
Stored Fields和Document Values
-
兩種結構爲不同的使用場景優化
- 少量文件獲取多個字段:Stored Fields
- 大量文件獲取少量字段:Document Values
-
-
文件格式的祕密
-
不能忘的規則
-
保存文件的句柄
不要爲每個字段每個文件使用文件
-
避免磁盤尋址
磁盤尋址的時間大概爲~10ms
-
不要忽略文件系統的緩存
隨機訪問小文件還是可以的
-
使用輕壓縮
- 更少I/O
- 更小索引
- 文件系統緩存友好
-
-
編碼解碼
-
文件格式依賴與編碼解碼
-
默認的編碼格式已經優化內存與速度之間的關係
不要使用RAMDirectory、MemoryPostingsFormat、MemoryDocValuesFormat。
-
詳細信息參照
http://lucene.apache.org/core/4_5_1/core/org/apache/lucene/codecs/packagesummary.
html
-
-
合適的壓縮技術
-
Bit packing / vlnt encoding
- postings lists
- numeric doc values
-
LZ4
- code.google.com/p/lz4
- 輕量壓縮算法
- Stored fields, term vectors
-
FSTs
- Map<String, ?>
- 鍵共享前綴(prefix)和後綴(suffix)
- terms index
-
-
TermQuery的背後
-
Terms Index
在索引中查找相應的詞
- 在內存中FST存儲了詞的前綴prefix
- 提供詞在字典中的偏移量
- 在不存在時可以快速失敗
-
Terms Dictionary
-
跳到字典偏移的位置
壓縮是基於共享前綴的,與“BlockTree term dict”類似
-
順序讀取直到找到特定的Term
-
-
Postings List
-
跳到postings list偏移量對應位置
-
用改進的FOR(Frame of Reference)進行增量編碼
- 增量編碼
- 將塊拆分爲N=128個值的大小
- 每個塊使用位壓縮(bit packing)
- 如果有剩餘文檔,使用vlnt壓縮
-
-
Stored Fields
-
對一個子集的doc id,索引存於內存中
高效內存(monotonic)壓縮
二分查找
-
字段
順序存儲
使用16KB塊存儲壓縮
-
-
-
查詢過程小結
-
每個字段2次磁盤尋址
-
每個文件1次磁盤尋址(Stored Fields)
-
terms dict/postings lists都在文件系統的緩存中
此時不會發生磁盤尋址
-
“脈衝”優化
- 對於唯一的term,postings list存儲在Terms dict中
- 1次磁盤尋址
- 永遠作爲主鍵
-
性能
上圖中系統性能出現兩次下降,可能的情況是
-
索引增長超過文件系統緩存的大小
Stored Fields不再全部存儲於緩存中
-
Terms dict/Postings lists不全在緩存中
參考
參考來源:
SlideShare: What is in a Lucene index?
Youtube: What is in a Lucene index? Adrien Grand, Software Engineer, Elasticsearch
SlideShare: Elasticsearch From the Bottom Up
Youtube: Elasticsearch from the bottom up
Standford Edu: Faster postings list intersection via skip pointers
StackOverflow: how an search index works when querying many words?
StackOverflow: how does lucene calculate intersection of documents so fast?
Lucene and its magical indexes