1、实战问题

  • 问题1:es 中 scroll 游标不能与 collapse 共用,有什么办法可以实现 collapse 的效果?
  • 问题2:采集互联网数据有重复数据写入 Elasticsearch,如何去重后放到另外索引?

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}}
{"title":"南开录取通知书亮相,附赠嘉兴莲花种子、一封特殊的信","cont":"今天,南开大学晒出建党百年特别版录取通知书。","url":"https://baijiahao.baidu.com/s?id=1703334751082094750&wfr=spider&for=pc","publish_time":"2021-06-23 13:36"}
{"index":{"_id":3}}
{"title":"南开录取通知书亮相,附赠嘉兴莲花种子、一封特殊的信","cont":"今天,南开大学晒出建党百年特别版录取通知书。","url":"https://baijiahao.baidu.com/s?id=1703334751082094750&wfr=spider&for=pc","publish_time":"2021-06-23 13:36"}
{"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 去重
GET news/_search
{
"query": {
"match_all": {}
},
"collapse": {
"field": "title.keyword"
}
}

2.2 Elasticsearch scroll 不支持 collapse 确认

源码确认:

fingerprint filter 插件——Elasticsearch 去重必备利器_搜索引擎

的确不支持。

2.3 考虑新方案

原有的方案和思路都在 scroll 导出数据方面行不通的,只能考虑新的思路了。

这个问题扩展一下,如何让数据写入 Elasticsearch 前去重呢?

说一下我的 Mysql 到 Elasticsearch 同步实战思路:

fingerprint filter 插件——Elasticsearch 去重必备利器_es_02

  • 数据源:爬虫采集互联网数据(由于是采集数据,难免会有转载等重复数据)。
  • 源数据存储:Mysql。
  • 如何界定重复?基于:发文标题、发文时间、发文正文内容、发文url 组成字段的MD5值作为去重标记。

数据由 Mysql 同步到 Elasticsearch 如何实现去重?

其实也很简单,一旦有了MD5值,将MD5值作为写入 Elasticsearch 的文档 id,就可以完成 Mysql 数据到 Elasticsearch 的去重同步处理。

而下面要着重讲解的 logstash fingerprint filter 插件实现数据去重处理,就是基于刚才的思路实现的。

3、logstash fingerprint filter 插件介绍

fingerprint:中文直译为"指纹"。

fingerprint filter 插件——Elasticsearch 去重必备利器_sms_03

​https://www.elastic.co/guide/en/logstash/current/plugins-filters-fingerprint.html​

3.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 个核心插件的中间一个插件。

fingerprint filter 插件——Elasticsearch 去重必备利器_es_04

其核心功能:创建一个或多个字段的一致哈希(指纹)并将结果存储在新字段中。

当文档插入 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 同步实操

fingerprint filter 插件——Elasticsearch 去重必备利器_es_05

4.3 成功标记

fingerprint filter 插件——Elasticsearch 去重必备利器_搜索引擎_06

fingerprint filter 插件——Elasticsearch 去重必备利器_es_07

5、小结

fingerprint filter 插件是基于现实业务问题而开发的,解决写入去重或者导出去重的业务痛点。我们再看开头两个问题。

  • 问题1 答案:不用 collapse,用 fingerprint filter 插件将数据转存为另外索引,然后 scroll 遍历输出就可以。
  • 问题2 答案:用 fingerprint filter 插件将数据转存为另外索引即可。

fingerprint filter 插件较开头两种去重方案优势体现在:

  • 能将去重后的数据独立存储为一个索引,且无需额外操作。
  • 方便业务单独处理数据。

欢迎留言交流一下您的去重思考。

参考

​https://alexmarquardt.com/tag/deduplicate/​