Lucene
lucene的學習目的主要是為了更好的理解ES的原理,重點理解兩個知識點:
1.倒排索引
2.數據分段
倒排索引
搜索的核心需求是全文檢索仍翰,全文檢索簡單來說就是要在大量文檔中找到包含某個單詞出現的位置,在傳統關系型數據庫中,數據檢索只能通過 like 來實現,例如需要在員工數據中查詢姓王的的員工歧匈,需要通過如下 sql 實現:
select * from hotel_table where hotel_name like '王%';
這種實現方式實際會存在很多問題:
1.無法使用數據庫索引,需要全表掃描砰嘁,性能差
2.搜索效果差件炉,只能首尾位模糊匹配,無法實現復雜的搜索需求
3.無法得到文檔與搜索條件的相關性
搜索的核心目標實際上是保證搜索的效果和性能矮湘,elasticsearch使用一種稱為倒排索引的數據結構斟冕,用于快速的全文搜索。一個倒排索引有文檔中所有不重復詞的列表構成缅阳,每個詞都有一個包含它的文檔列表宫静。
倒排索引是區(qū)別于正排索引的概念:
正排索引:
是以文檔對象的唯一 ID 作為索引,以文檔內容作為記錄的結構。
倒排索引:
Inverted index孤里,指的是將文檔內容中的單詞作為索引伏伯,將包含該詞的文檔 ID 作為記錄的結構。
參考如下:
當有一組員工數據的時候捌袜,可以存在兩個倒排數據 一個是基于年領倒排说搅,一個是基于性別倒排。
下面通過一個例子來說明下倒排索引的生成過程虏等。
假設目前有以下兩個文檔內容:
蘇州街維亞大廈
桔子酒店蘇州街店
其處理步驟如下:
1弄唧、正排索引給每個文檔進行編號,作為其唯一的標識霍衫。
a.首先要對字段的內容進行分詞候引,分詞就是將一段連續(xù)的文本按照語義拆分為多個單詞,這里兩個文檔包含的關鍵詞有:蘇州街敦跌、維亞大廈.....
b.然后按照單詞來作為索引澄干,對應的文檔 id 建立一個鏈表,就能構成上述的倒排索引結構柠傍。
有了倒排索引麸俘,能快速、靈活地實現各類搜索需求惧笛。整個搜索過程中我們不需要做任何文本的模糊匹配从媚。
例如,如果需要在上述兩個文檔中查詢 蘇州街桔子 患整,可以通過分詞后通過 蘇州街 查到 1拜效、2,通過 桔子 查到2各谚,然后再進行取交取并等操作得到最終結果拂檩。
倒排索引的結構
根據倒排索引的概念,我們可以用一個 Map來簡單描述這個結構嘲碧。這個 Map 的 Key 的即是分詞后的單詞,這里的單詞稱為 Term父阻,這一系列的 Term 組成了倒排索引的第一個部分 —— Term Dictionary (索引表愈涩,可簡稱為 Dictionary)。
倒排索引的另一部分為 Postings List(記錄表)加矛,也對應上述 Map 結構的 Value 部分集合履婉。
記錄表由所有的 Term 對應的數據(Postings) 組成,它不僅僅為文檔 id 信息斟览,可能包含以下信息:
文檔 id(DocId, Document Id)毁腿,包含單詞的所有文檔唯一 id,用于去正排索引中查詢原始數據。
詞頻(TF已烤,Term Frequency)鸠窗,記錄 Term 在每篇文檔中出現的次數,用于后續(xù)相關性算分胯究。
位置(Position)稍计,記錄 Term 在每篇文檔中的分詞位置(多個),用于做詞語搜索(Phrase Query)裕循。
偏移(Offset)臣嚣,記錄 Term 在每篇文檔的開始和結束位置,用于高亮顯示等剥哑。
Lucene 倒排索引實現
全文搜索引擎在海量數據的情況下是需要存儲大量的文本硅则,所以面臨以下問題:
Dictionary 是比較大的(比如我們搜索中的一個字段可能有上千萬個 Term)
Postings 可能會占據大量的存儲空間(一個Term多的有幾百萬個doc)
因此上面說的基于 Map 的實現方式幾乎是不可行的。
在海量數據背景下株婴,倒排索引的實現直接關系到存儲成本以及搜索性能怎虫。
為此,Lucene 引入了多種巧妙的數據結構和算法督暂。其倒排索引實現擁有以下特性:
以較低的存儲成本存儲在磁盤 (索引大小大約為被索引文本的20-30%)
快速讀寫
下面將根據倒排索引的結構揪垄,按 Posting List 和 Terms Dictionary 兩部分來分析 Lucene 中的實現。
Posting List 實現
PostingList 包含文檔 id逻翁、詞頻饥努、位置等多個信息,這些數據之間本身是相對獨立的八回,因此 Lucene 將 Postings List 被拆成三個文件存儲:
.doc后綴文件:記錄 Postings 的 docId 信息和 Term 的詞頻
.pay后綴文件:記錄 Payload 信息和偏移量信息
.pos后綴文件:記錄位置信息
基本所有的查詢都會用 .doc 文件獲取文檔 id酷愧,且一般的查詢僅需要用到 .doc 文件就足夠了,只有對于近似查詢等位置相關的查詢則需要用位置相關數據缠诅。
三個文件整體實現差不太多溶浴,這里以.doc 文件為例分析其實現。
.doc 文件存儲的是每個 Term 對應的文檔 Id 和詞頻管引。每個 Term 都包含一對 TermFreqs 和 SkipData 結構士败。
其中 TermFreqs 存放 docId 和詞頻信息,SkipData 為跳表信息褥伴,用于實現 TermFreqs 內部的快速跳轉谅将。
Term Dictionary 實現
Terms Dictionary(索引表)存儲所有的 Term 數據,同時它也是 Term 與 Postings 的關系紐帶重慢,存儲了每個 Term 和其對應的 Postings 文件位置指針饥臂。
倒排查詢邏輯
在介紹了索引表和記錄表的結構后,就可以得到 Lucene 倒排索引的查詢步驟:
1.通過 Term Index 數據(.tip文件)中的 StartFP 獲取指定字段的 FST
2.通過 FST 找到指定 Term 在 Term Dictionary(.tim 文件)可能存在的 Block
3.將對應 Block 加載內存似踱,遍歷 Block 中的 Entry隅熙,通過后綴(Suffix)判斷是否存在指定 Term
4.存在則通過 Entry 的 TermStat 數據中各個文件的 FP 獲取 Posting 數據
5.如果需要獲取 Term 對應的所有 DocId 則直接遍歷 TermFreqs稽煤,如果獲取指定 DocId 數據則通過 SkipData 快速跳轉
數據分段
在早期的全文檢索中為整個文檔集合建立了一個很大的倒排索引,并將其寫入磁盤中囚戚,如果索引有更新酵熙,就需要重新全量創(chuàng)建一個索引來替換原來的索引。
這種方式在數據量很大時效率很低弯淘,并且由于創(chuàng)建一次索引的成本很高绿店,所以對數據的更新不能過于頻繁,也就不能保證時效性庐橙。
現在假勿,在搜索中引入了段的概念(將一個索引文件拆分為多個子文件,則每個子文件叫作段)态鳖,每個段都是一個獨立的可被搜索的數據集转培,并且段具有不變性,一旦索引的數據被寫入硬盤浆竭,就不可再修改浸须。
寫操作
在分段的思想下,對數據寫操作的過程如下邦泄。
新增:
當有新的數據需要創(chuàng)建索引時删窒,由于段的不變性,所以選擇新建一個段來存儲新增的數據顺囊。
刪除:
當需要刪除數據時肌索,由于數據所在的段只可讀,不可寫特碳,所以Lucene在索引文件下新增了一個.del的文件诚亚,用來專門存儲被刪除的數據id。當查詢時午乓,被刪除的數據還是可以被查到的站宗,只是在進行文檔鏈表合并時,才把已經刪除的數據過濾掉益愈。被刪除的數據在進行段合并時才會真正被移除梢灭。
更新:
更新的操作其實就是刪除和新增的組合,先在.del文件中記錄舊數據蒸其,再在新段中添加一條更新后的數據敏释。
段不變性的優(yōu)點
1.不需要鎖。因為數據不會更新枣接,所以不用考慮多線程下的讀寫不一致情況。
2.可以常駐內存缺谴。段在被加載到內存后但惶,由于具有不變性耳鸯,所以只要內存的空間足夠大,就可以長時間駐存膀曾,大部分查詢請求會直接訪問內存县爬,而不需要訪問磁盤,使得查詢的性能有很大的提升添谊。
3.緩存友好财喳。在段的聲明周期內始終有效,不需要在每次數據更新時被重建斩狱。
4.增量創(chuàng)建耳高。分段可以做到增量創(chuàng)建索引,可以輕量級地對數據進行更新所踊,由于每次創(chuàng)建的成本很低泌枪,所以可以頻繁地更新數據,使系統接近實時更新秕岛。
段不變性的缺點
1.當對數據進行刪除時碌燕,舊數據不會被馬上刪除,而是在.del文件中被標記為刪除继薛。而舊數據只能等到段更新時才能真正被移除修壕,這樣會有大量的空間浪費。
2.更新遏考。更新數據由刪除和新增這兩個動作組成慈鸠。若有一條數據頻繁更新,則會有大量的空間浪費诈皿。
3.由于索引具有不變性林束,所以每次新增數據時,都需要新增一個段來存儲數據稽亏。當段的數量太多時壶冒,對服務器的資源(如文件句柄)的消耗會非常大,查詢的性能也會受到影響截歉。
4.在查詢后需要對已經刪除的舊數據進行過濾胖腾,這增加了查詢的負擔。
準實時的搜索引擎
為了提升寫的性能瘪松,Lucene并沒有每新增一條數據就增加一個段咸作,而是采用延遲寫的策略,每當有新增的數據時宵睦,就將其先寫入內存中记罚,然后批量寫入磁盤中。若有一個段被寫到硬盤壳嚎,就會生成一個提交點桐智,提交點就是一個用來記錄所有提交后的段信息的文件末早。一個段一旦擁有了提交點,就說明這個段只有讀的權限说庭,失去了寫的權限然磷;相反,當段在內存中時刊驴,就只有寫數據的權限姿搜,而不具備讀數據的權限,所以也就不能被檢索了捆憎。從嚴格意義上來說舅柜,Lucene或者Elasticsearch并不能被稱為實時的搜索引擎,只能被稱為準實時的搜索引擎攻礼。
寫索引的流程如下
-
新數據被寫入時业踢,并沒有被直接寫到硬盤中,而是被暫時寫到內存中礁扮。Lucene默認是一秒鐘知举,或者當內存中的數據量達到一定階段時,再批量提交到磁盤中太伊,當然雇锡,默認的時間和數據量的大小是可以通過參數控制的。通過延時寫的策略僚焦,可以減少數據往磁盤上寫的次數锰提,從而提升整體的寫入性能。
在達到觸發(fā)條件以后芳悲,會將內存中緩存的數據一次性寫入磁盤中立肘,并生成提交點。
-
清空內存名扛,等待新的數據寫入谅年。
段合并策略
雖然分段比每次都全量創(chuàng)建索引有更高的效率,但由于在每次新增數據時都會新增一個段肮韧,所以經過長時間的積累融蹂,會導致在索引中存在大量的段,當索引中段的數量太多時弄企,不僅會嚴重消耗服務器的資源超燃,還會影響檢索的性能。
因為索引檢索的過程是:
查詢所有段中滿足查詢條件的數據拘领,然后對每個段里查詢的結果集進行合并意乓,所以為了控制索引里段的數量,我們必須定期進行段合并操作约素。
但是如果每次合并全部的段届良,則將造成很大的資源浪費本涕,特別是“大段”的合并。
所以Lucene現在的段合并思路是:
根據段的大小先將段進行分組伙窃,再將屬于同一組的段進行合并。但是由于對超級大的段的合并需要消耗更多的資源样漆,所以Lucene會在段的大小達到一定規(guī)模为障,或者段里面的數據量達到一定條數時,不會再進行合并放祟。
所以Lucene的段合并主要集中在對中小段的合并上鳍怨,這樣既可以避免對大段進行合并時消耗過多的服務器資源,也可以很好地控制索引中段的數量跪妥。
段合并的主要參數
mergeFactor:每次合并時參與合并的段的最少數量鞋喇,當同一組的段的數量達到此值時開始合并,如果小于此值則不合并眉撵,這樣做可以減少段合并的頻率侦香,其默認值為10。
SegmentSize:指段的實際大小纽疟,單位為字節(jié)罐韩。
minMergeSize:小于這個值的段會被分到一組,這樣可以加速小片段的合并污朽。
maxMergeSize:若一個段的文本數量大于此值散吵,就不再參與合并,因為大段合并會消耗更多的資源蟆肆。
段合并相關的動作
- 對索引中的段進行分組矾睦,把大小相近的段分到一組,主要由LogMergePolicyl類來處理炎功。
- 將屬于同一分組的段合并成一個更大的段枚冗。
在段合并前對段的大小進行了標準化處理,通過logMergeFactorSegmentSize
計算得出亡问,其中官紫,MergeFactor表示一次合并的段的數量,Lucene默認該數量為10州藕;SegmentSize表示段的實際大小束世。通過上面的公式計算后,段的大小更加緊湊床玻,對后續(xù)的分組更加友好毁涉。
段分組的步驟
根據段生成的時間對段進行排序,然后根據上述標準化公式計算每個段的大小并且存放到段信息中锈死,后面用到的描述段大小的值都是標準化后的值贫堰。
-
在數組中找到最大的段穆壕,然后生成一個由最大段的標準化值作為上限,減去LEVEL_LOG_SPAN(默認值為0.75)后的值作為下限的區(qū)間其屏。小于等于上限并且大于下限的段喇勋,都被認為是屬于同一個組的段,可以合并偎行。
-
在確定一個分組的上下限值后川背,就需要查找屬于這個分組的段了,具體過程是:創(chuàng)建兩個指針(在這里使用指針的概念是為了更好地理解)start和end蛤袒,start指向數組的第1個段熄云,end指向第start+MergeFactor個段,然后從end逐個向前查找落在區(qū)間的段妙真,當找到第1個滿足條件的段時缴允,則停止,并把當前段到start之間的段統一分到一個組珍德,無論段的大小是否滿足當前分組的條件练般。
這樣做的好處如下:
1)增加段合并的概率,避免由于段的大小參差不齊導致段難以合并锈候。
2)簡化了查找的邏輯踢俄,使代碼的運行效率更高。
在分組找到后晴及,需要排除不參加合并的“超大”段都办,然后判斷剩余的段是否滿足合并的條件,mergeFactor=5虑稼,而找到的滿足合并條件的段的個數為4琳钉,所以不滿足合并的條件,暫時不進行合并蛛倦,繼續(xù)尋找下一個分組的上下限歌懒。
由于在第4步并沒有找到滿足段合并的段的數量,所以這一分組的段不滿足合并的條件溯壶,繼續(xù)進行下一分組段的查找及皂。具體過程是:將start指向end,在剩下的段(從end指向的元素開始到數組的最后一個元素)中尋找最大的段且改,在找到最大的值后再減去LEVEL_LOG_SPAN的值验烧,再生成一個分組的區(qū)間值;然后把end指向數組的第start+MergeFactor個段又跛,逐個向前查找第1個滿足條件的段碍拆;重復第3步和第4步。
-
如果一直沒有找到滿足合并條件的段,則一直重復第5步感混,直到遍歷完整個數組端幼。
ELK
ELK是三個開源軟件的縮寫,分別表示:Elasticsearch , Logstash, Kibana , 它們都是開源軟件弧满。新增了一個FileBeat婆跑,它是一個輕量級的日志收集處理工具(Agent),Filebeat占用資源少庭呜,適合于在各個服務器上搜集日志后傳輸給Logstash洽蛀,官方也推薦此工具。
Elasticsearch是個開源分布式搜索引擎疟赊,提供搜集、分析峡碉、存儲數據三大功能近哟。它的特點有:分布式,零配置鲫寄,自動發(fā)現吉执,索引自動分片,索引副本機制地来,restful風格接口戳玫,多數據源,自動搜索負載等未斑。主要負責將日志索引并存儲起來咕宿,方便業(yè)務方檢索查詢。
Logstash 主要是用來日志的搜集蜡秽、分析府阀、過濾日志的工具充蓝,支持大量的數據獲取方式蔫骂。一般工作方式為c/s架構,client端安裝在需要收集日志的主機上卷仑,server端負責將收到的各節(jié)點日志進行過濾寞蚌、修改等操作在一并發(fā)往elasticsearch上去田巴。是一個日志收集、過濾挟秤、轉發(fā)的中間件壹哺,主要負責將各條業(yè)務線的各類日志統一收集、過濾后艘刚,轉發(fā)給 Elasticsearch 進行下一步處理斗躏。
Kibana 也是一個開源和免費的工具,Kibana可以為 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以幫助匯總啄糙、分析和搜索重要數據日志笛臣。
Filebeat隸屬于Beats。目前Beats包含四種工具:
Packetbeat(搜集網絡流量數據)
Topbeat(搜集系統隧饼、進程和文件系統級別的 CPU 和內存使用情況等數據)
Filebeat(搜集文件數據)
Winlogbeat(搜集 Windows 事件日志數據)
Filebeat工作原理
Filebeat由兩個主要組件組成:prospectors 和 harvesters沈堡。這兩個組件協同工作將文件變動發(fā)送到指定的輸出中。
1燕雁、Harvester(收割機):負責讀取單個文件內容诞丽。每個文件會啟動一個Harvester,每個Harvester會逐行讀取各個文件拐格,并將文件內容發(fā)送到制定輸出中僧免。Harvester負責打開和關閉文件,意味在Harvester運行的時候捏浊,文件描述符處于打開狀態(tài)懂衩,如果文件在收集中被重命名或者被刪除,Filebeat會繼續(xù)讀取此文件金踪。所以在Harvester關閉之前浊洞,磁盤不會被釋放。默認情況filebeat會保持文件打開的狀態(tài)胡岔,直到達到close_inactive(如果此選項開啟法希,filebeat會在指定時間內將不再更新的文件句柄關閉,時間從harvester讀取最后一行的時間開始計時靶瘸。若文件句柄被關閉后苫亦,文件發(fā)生變化,則會啟動一個新的harvester怨咪。關閉文件句柄的時間不取決于文件的修改時間著觉,若此參數配置不當,則可能發(fā)生日志不實時的情況惊暴,由scan_frequency參數決定饼丘,默認10s。Harvester使用內部時間戳來記錄文件最后被收集的時間辽话。例如:設置5m肄鸽,則在Harvester讀取文件的最后一行之后,開始倒計時5分鐘油啤,若5分鐘內文件無變化典徘,則關閉文件句柄。默認5m)益咬。
2逮诲、Prospector(勘測者):負責管理Harvester并找到所有讀取源。
Prospector會找到/apps/logs/*目錄下的所有info.log文件,并為每個文件啟動一個Harvester梅鹦。Prospector會檢查每個文件裆甩,看Harvester是否已經啟動,是否需要啟動齐唆,或者文件是否可以忽略嗤栓。若Harvester關閉,只有在文件大小發(fā)生變化的時候Prospector才會執(zhí)行檢查箍邮。只能檢測本地的文件茉帅。
ES用于安全
在傳統安全領域,企業(yè)通常會借助防火墻锭弊,殺毒軟件等為企業(yè)構造起一套固若金湯的安全防御體系堪澎,然而即使在如此嚴密的防護之下,仍然無法完全保證內部數據的安全味滞,尤其是當面臨內部威脅時樱蛤。這時,根據已有安全數據進行安全分析桃犬,及時發(fā)現并處理威脅就顯得尤為重要。
然而行楞,現代企業(yè)的安全數據已隨著日益蓬勃發(fā)展的信息網絡技術而迅速膨脹攒暇,對海量安全數據的采集,處理子房,存儲形用,查詢等正日益困擾著企業(yè)安全分析團隊。
而ES正是為應對海量數據的采集和檢索而生的证杭,將ES應用于安全分析領域可以非常便捷高效地解決安全分析領域海量數據的存儲和檢索問題田度。
1.數據采集
數據采集是安全分析的第一步,也是至關重要的一步解愤。安全分析所需的數據來源多種多樣镇饺,有網絡數據,也有文件數據送讲,有本機數據還有云端數據奸笤。Elastic Stack提供的Beat工具包含了豐富的數據采集工具,可以方便地應用于各種數據采集場景哼鬓。下表是安全分析中需要采集的各種數據以及ES下對應的采集方式
2.數據標準化
安全分析的數據來源多種多樣监右,不同來源的數據中表示相同含義的字段在名稱,類型上各不相同异希,這就導致了在進行數據檢索分析時,為了檢索不同數據源中的同類數據,可能要兼容性地寫多個查詢條件健盒,這給數據分析帶來了不小的麻煩。
在實際的安全分析中為了兼容來自不同數據源的數據。
為了解決這個問題扣癣,Elastic建立ECS(Elastic Common Schema)項目惰帽,采用專業(yè)的分類方法,對字段進行統一命名搏色,且Elastic生態(tài)相關組件均遵循這一命名規(guī)格善茎,使得對不同數據源的檢索得以簡化。
3.數據增強
除了對數據進行標準化以外频轿, Elastic Stack中的Beat垂涯、Logstash、Elasticsearch等組件都可以對數據進行增強航邢,根據現有環(huán)境或數據耕赘,通過數據處理衍生出一些相關數據,為安全分析提供更為直接詳細的信息膳殷。
Beats https://www.elastic.co/guide/en/beats/filebeat/6.4/filtering-and-enhancing-data.html
Logstash https://www.elastic.co/guide/en/logstash/6.4/filter-plugins.html
Elasticsearch https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest-processors.html