2、排查思路
2.1 Elasticsearch 去重的幾種方式
之前我有文章解讀:Elasticsearch6.X 去重詳解
方式一:terms 指定字段聚合 + top_hits 子聚合。
方式二:collapse 折疊去重。
拿個實戰列子看一下:
當下正值聯考出成績,我們拿新聞事件資訊作為資料來源。
如下文檔_id:1、_id:2、_id:3 是一模一樣的資料;_id: 4 是獨立資料。
也就是說:去重後資料分兩組,一組:[1,2,3]; 另外一組:[4]。
PUT news/_bulk
{"index":{"_id":1}}
{"title":"南開錄取通知書亮相,附贈嘉興蓮花種子、一封特殊的信","cont":"今天,南開大學曬出建黨百年特别版錄取通知書。","url":"https://baijiahao.baidu.com/s?id=1703334751082094750&wfr=spider&for=pc","publish_time":"2021-06-23 13:36"}
{"index":{"_id":2}}
{"index":{"_id":3}}
{"index":{"_id":4}}
{"title":"建黨百年特别版!南開大學錄取通知書送兩粒嘉興蓮花種子","cont":"@南開大學 6月23日消息,建黨百年特别版南開大學錄取通知書揭秘!","url":"https://www.163.com/dy/article/GD69KNIR0514R9P4.html","publish_time":"2021-06-23 13:25:00"}
# top_hits 子聚合去重
GET news/_search
{
"query": {
"match_all": {}
},
"aggs": {
"type": {
"terms": {
"field": "title.keyword",
"size": 10
},
"aggs": {
"title_top": {
"top_hits": {
"_source": {
"includes": [
"title"
]
},
"sort": [
{
"title.keyword": {
"order": "desc"
}
}
],
"size": 1
}
}
}
}
"size": 0
}
# collapse 去重
"collapse": {
"field": "title.keyword"
}
2.2 Elasticsearch scroll 不支援 collapse 确認
源碼确認:
的确不支援。2.3 考慮新方案
原有的方案和思路都在 scroll 導出資料方面行不通的,隻能考慮新的思路了。
這個問題擴充一下,如何讓資料寫入 Elasticsearch 前去重呢?
說一下我的 Mysql 到 Elasticsearch 同步實戰思路:
資料源:爬蟲采集網際網路資料(由于是采集資料,難免會有轉載等重複資料)。
源資料存儲:Mysql。
如何界定重複?基于:發文标題、發文時間、發文正文内容、發文url 組成字段的MD5值作為去重标記。
資料由 Mysql 同步到 Elasticsearch 如何實作去重?
其實也很簡單,一旦有了MD5值,将MD5值作為寫入 Elasticsearch 的文檔 id,就可以完成 Mysql 資料到 Elasticsearch 的去重同步處理。
而下面要着重講解的 logstash fingerprint filter 插件實作資料去重處理,就是基于剛才的思路實作的。
3、logstash fingerprint filter 插件介紹
fingerprint:中文直譯為"指紋"。
https://www.elastic.co/guide/en/logstash/current/plugins-filters-fingerprint.html3.1 fingerprint filter 插件版本
官方文檔強調:
Versioned plugin documentation is not available for plugins released prior to Logstash 6.0.
這是 Logstash 6.X 之後才有的功能。
3.2 fingerprint filter 插件用途
fingerprint filter 插件是 logstash filter 強大環節中的 58 個核心插件的中間一個插件。
其核心功能:建立一個或多個字段的一緻哈希(指紋)并将結果存儲在新字段中。
當文檔插入 Elasticsearch 時,可以使用此插件建立一緻的文檔 ID。
也就是說,如果兩個或者後續多個文檔的指紋一緻,則寫入 Elasticsearch 的 _id 一緻(前提 ES ID是明确指定使用指紋),是以相同指紋資料寫入 Elasticsearch 會覆寫,間接實作了寫入去重。
下面我們先實戰,再根據實戰講解核心參數意思,大家了解可能更順暢、通透一些。
4、logstash fingerprint filter 去重實戰
4.1 同步腳本
寫在配置檔案:logstash_print.conf 中(配置檔案名稱自己定義就可以)。
input {
# Read all documents from Elasticsearch
elasticsearch {
hosts => "172.21.0.14:19022"
index => "news"
query => '{ "sort": [ "_doc" ] }'
filter {
fingerprint {
key => "1234ABCD"
method => "SHA256"
source => ["title", "cont", "url", "publish_time"]
target => "[@metadata][generated_id]"
concatenate_sources => true
output {
stdout { codec => dots }
elasticsearch {
hosts => "172.21.0.14:19022"
index => "news_after_fingerprint"
document_id => "%{[@metadata][generated_id]}"
4.1.1 腳本講解
logstash 腳本大家就記住三段論。
第一:input,代表輸入(讀取端),本執行個體自然是基于 Elasticsearch 讀。
第二:filter,代表中間處理,那就是指紋處理部分。
第三:output,代表輸出(寫入端),本執行個體還是寫入 Elasticsearch,隻不過會寫入新的索引 news_after_fingerprint。
4.1.2 filter 環節核心參數講解
key => "1234ABCD",代表目前指紋的唯一值。
method => "SHA256",指紋生成方式。
source => ["title", "cont", "url", "publish_time"],生成指紋的基礎字段名稱。
target => "[@metadata][generated_id]":将存儲生成的指紋的字段的名稱,後面output 環節會使用。該字段的任何目前内容都将被覆寫。
concatenate_sources => true
如果為true 且 method 不是 UUID 或 PUNCTUATION 時,插件會在進行指紋計算之前将 source 選項中給出的所有字段的名稱和值連接配接成一個字元串。
如果給出 false 和多個源字段,則目标字段将是最後一個源字段的單個指紋。
4.2 同步實操
4.3 成功标記
5、小結
fingerprint filter 插件是基于現實業務問題而開發的,解決寫入去重或者導出去重的業務痛點。我們再看開頭兩個問題。
問題1 答案:不用 collapse,用 fingerprint filter 插件将資料轉存為另外索引,然後 scroll 周遊輸出就可以。
問題2 答案:用 fingerprint filter 插件将資料轉存為另外索引即可。
fingerprint filter 插件較開頭兩種去重方案優勢展現在:
能将去重後的資料獨立存儲為一個索引,且無需額外操作。
友善業務單獨處理資料。
歡迎留言交流一下您的去重思考。
參考
https://alexmarquardt.com/tag/deduplicate/