本文作者:劉曉國,Elastic公司社群布道師。新加坡國立大學碩士,西北工業大學碩士,曾就職于新加坡科技,康柏電腦,通用汽車,愛立信,諾基亞,Linaro,Ubuntu,Vantiq等企業。
*如果你想一站式快速體驗 Elasticsearch 所有功能(包括 X-pack 付費功能),開通
阿裡雲 Elasticsearch 1核2G,即可首月免費試用
*
背景:Elasticsearch 索引
在介紹重複資料删除解決方案之前,讓我們簡要介紹一下 Elasticsearch 的索引編制過程。 Elasticsearch 提供了一個 REST API 來為你的文檔建立索引。你可以選擇提供唯一代表您的文檔的 ID,也可以讓 Elasticsearch 為你生成ID。如果您将
HTTP PUT與索引 API 一起使用,Elasticsearch 希望您提供一個 ID。如果已經存在具有相同 ID 的文檔,Elasticsearch 将用你剛才提供的文檔替換現有内容-最後索引的文檔将獲勝。如果使用 POST 動詞,則即使語料庫中已經存在内容,Elasticsearch 也會生成具有新ID的新文檔。例如,假設你剛在一秒鐘之前為部落格文章建立了索引,并使用 POST 動詞重新發送了同一篇部落格文章,Elasticsearch 建立了另一個具有相同内容但新具有 ID 的文檔。
雖然 Elasticsearch 提供了一個顯式的 _update API,可以将其用作潛在的解決方法,但我們将把本文重點放在索引 API 上。
Logstash 的 Elasticsearch 輸出使用索引API,并且預設情況下不希望提供 ID。是以,它将每個單個事件視為單獨的文檔。但是,有一個選項可讓你輕松為 Logstash 中的每個事件設定唯一的 ID。
定義你的ID
如果你的資料源已經有一個ID,那麼在索引到 Elasticsearch 之前很容易将其設定為文檔ID。 例如,JDBC 輸入的使用者可以輕松地将源表中的主鍵用作 Elasticsearch ID。 使用字段引用文法,可以在輸出部分中直接設定文檔 ID:
output {
elasticsearch {
hosts => "example.com"
document_id => "%{[upc_code]}"
}
}
删除重複的相似内容
如前所述,在你的用例中,重複的内容可能是不可接受的。使用稱為指紋的概念和 Logstash 指紋
過濾器(fingerprint),您可以建立一個稱為指紋的新字元串字段,以唯一地辨別原始事件。指紋過濾器可以将原始事件中的一個或多個字段(預設為消息字段)作為源來建立一緻的哈希值 (hash)。一旦建立了這些指紋,你就可以将其用作下遊Elasticsearch輸出中的文檔ID。這樣,Elasticsearch 将僅在比較指紋後更新或覆寫現有文檔内容,但絕不會複制它們。如果你想考慮更多字段以進行删除重複資料,則可以使用 concatenate_sources 選項。
指紋過濾器具有多種算法,您可以選擇建立此一緻的哈希(hash)。請參閱文檔,因為每個函數的哈希強度不同,可能需要其他選項。在下面的示例中,我們使用 MURMUR3 方法從消息字段建立哈希并将其設定在中繼資料字段中。中繼資料字段不會發送到輸出,是以它們提供了一種在處理管道中的事件時臨時存儲資料的有效方法。
filter {
fingerprint {
source => "message"
target => "[@metadata][fingerprint]"
method => "MURMUR3"
}
}
output {
elasticsearch {
hosts => "example.com"
document_id => "%{[@metadata][fingerprint]}"
}
}
如果使用任何加密哈希函數算法(例如SHA1,MD5),則需要提供密鑰選項。 密鑰可以是用于計算 HMAC 的任意字元串。
filter {
fingerprint {
source => "message"
target => "[@metadata][fingerprint]"
method => "SHA1",
key => "Log analytics",
base64encode => true
}
}
output {
elasticsearch {
hosts => "example.com"
document_id => "%{[@metadata][fingerprint]}"
}
}
密鑰的其他示例可以是 departmentID,組織 ID 等。
意外重複:從 Logstash 生成 UUID
先前的用例涉及内容的有意識地删除重複資料。在某些部署中,尤其是 Logstash 與可確定至少傳遞一次的持久性隊列或其他排隊系統一起使用時,Elasticsearch 中可能存在重複項。如果 Logstash 在處理過程中崩潰,則重新啟動時将重播隊列中的資料-這可能導緻重複。為了減少這種情況造成的重複,可以對每個事件使用 UUID。這裡的重點是,在将資料序列化到消息隊列之前,需要在生産方(即釋出到排隊系統的 Logstash 執行個體)上生成UUID。這樣,Logstash使用者在從崩潰還原或重新啟動時需要重新處理事件時,将保留相同的事件ID。
如果你的源資料沒有唯一辨別符,則可以使用同一指紋過濾器來生成 UUID。請記住,此方法不考慮事件本身的内容,而是為每個事件生成 version 4 UUID。
filter {
fingerprint {
target => "%{[@metadata][uuid]}"
method => "UUID"
}
}
output {
elasticsearch {
hosts => "example.com"
document_id => "%{[@metadata][uuid]}"
}
}
如果在 Logstash 生産者和使用者之間使用隊列,則必須顯式複制@metadata字段,因為它們不會持久化到輸出中。 另外,你可以使用以下正常字段:
filter {
fingerprint {
target => "generated_id"
method => "UUID"
}
}
output {
kafka {
brokers => "example.com"
...
}
}
從消費者方面,您可以隻使用:
input {
kafka {
brokers => "example.com"
}
}
output {
elasticsearch {
hosts => "example.com"
document_id => "%{[generated_id]}"
}
}
例子
在下面,我們用一個實際的例子來展示,這個是如工作的。首先讓我們先建立一個叫做 logstash_fingerprint.conf 的 Logstash 配置檔案:
logstash_fingerprint.conf
input {
http {
id => "data_http_input"
}
}
filter {
fingerprint {
source => [ "sensor_id", "date"]
target => "[@metadata][fingerprint]"
method => "SHA1"
key => "liuxg"
concatenate_sources => true
base64encode => true
}
}
output {
stdout {
codec => rubydebug
}
elasticsearch {
manage_template => "false"
index => "fingerprint"
hosts => "localhost:9200"
document_id => "%{[@metadata][fingerprint]}"
}
}
在這裡,我們使用 http input 來收集資料。在這裡,我們使用 sensor_id 及 date 這兩個字段來生成一個 fingerprint。也就是說,隻有這兩個字段是一樣的,那麼無論我們輸入多少次資料,那麼在 Elasticsearch 中将不會有新的資料生成,因為它們的 ID 都是一樣的。 我們啟動 Logstash:
sudo ./bin/logstash -f ~/data/fingerprint/logstash_fingerprint.conf
我們可以在另外一個 console 中打入如下的指令:
curl -XPOST --header "Content-Type:application/json"http://localhost:8080/" -d '{"sensor_id":1, "date": "2015-01-01", "reading":16.24}'
這個時候,我們可以在 Logstash 的 console 中檢視到:

我們在 Kibana 的 Dev Tools 中進行檢視:
GET _cat/indices
我們可以看到有一個新的 fingerprint 的索引已經生産了。
我們檢視 fingerprint 的文檔數:
GET fingerprint/_count
結果顯示:
{
"count" : 1,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
我們在另外一個 console 中打入無數次的如下的指令:
我們發現,隻要是 sensor_id 和 date 的值都是一樣的,那麼 fingerprint 的文檔數永遠是 1。當然你也可以更新其它字段的值,比如 reading 字段的值為20,那麼新的值将會在裡面得以展現。這個操作相當于更新的操作。
如果我們改動一下 sensor_id 的值為2,也就是:
curl -XPOST --header "Content-Type:application/json"http://localhost:8080/" -d '{"sensor_id":2, "date": "2015-01-01", "reading":16.24}'
那麼我們重新檢視 fingerprint 索引的文檔數:
GET fingerprint/_count
上面顯示文檔的數值為2。也就是說,在索引 fingerprint 中,隻要是 sensor_id 及 date 的數值是一樣的,那麼我們将永遠隻有一個文檔,而且是永遠不會重複的。
結論
如你在本文中所看到的,指紋過濾器可以用于多種用途,并且是你應該在Logstash生态系統中熟悉的插件。它可以很友善地讓我們保持我們的文檔的唯一性,而不招緻重複的資料生成。
聲明:本文由原文作者“ Elastic 中國社群布道師——劉曉國”授權轉載,對未經許可擅自使用者,保留追究其法律責任的權利。
出處連結:
https://elasticstack.blog.csdn.net/.【
阿裡雲Elastic Stack】100%相容開源ES,獨有9大能力,提供免費 X-pack服務(單節點價值$6000)
相關活動
更多折扣活動,請
通路阿裡雲 Elasticsearch 官網 阿裡雲 Elasticsearch 商業通用版,1核2G ,SSD 20G首月免費 阿裡雲 Logstash 2核4G首月免費 下載下傳白皮書:Elasticsearch 八大經典場景應用