您好,请教个问题。我如今有2千多万的手机号码信息保存在es里。5个分片,3个节点。git
如今的需求是将后八位相同的号码匹配到一块儿,从新放到一个index里。组成情侣号。方便后续查询情侣号列表。redis
我目前的作法是用scroll查询出一万条,多线程循环一万条中的每条,去全库扫描---可是这种作法一分钟才能处理一万条。您有什么新的思路没。json
死磕Elasticsearch知识星球 https://t.zsxq.com/Iie66qV多线程
问题补充:索引存储了手机号,同时存储了插入时间。
app
后八位相同的号码即为情侣号。less
举例:运维
13011112222 13511112222 13711112222
缺点:script效率低一些ide
Elasticsearch自带reindex
功能就是实现索引迁移的,固然自定义读写也能够实现。性能
步骤 1:基于时间递增循环遍历,以起始的手机号为种子数据,知足后八位相同的加上标记flag=1。spa
步骤 2:循环步骤1,知足flag=1直接跳过,直到全部手机号遍历一遍。
步骤 3:将包含flag=1的字段,reindex到情侣号索引。
考虑到数据量级千万级别,全量聚合不现实。
能够,基于时间切片,取出最小时间戳、最大时间戳,根据数据总量和时间范围划分出时间间隔。
举例:以30分钟为单位切割千万级数据。
步骤 1:terms聚合后8位手机号。
terms聚合只返回对应:key,value值,默认value值由高到低排序。
key:表明手机号后8位,value:表明相同后8位的数据量。
步骤 2:top_hits子聚合取出手机号详情。
步骤 3:json解析识别出步骤2的全部手机号或_id。
步骤 4:reindex步骤3的_id数据到情侣号索引。
步骤 5:时间切片周期递增,直到全部数据遍历完毕。
举例:查询“11112222”,返回2.1列表的三个手机号。
方案1:wildcard模糊匹配。
优势:无需额外字段存储。
缺点:效率低。
方案2:ngram分词+match_phrase处理。
优势:效率高。
缺点:须要独立存储的后8位字段。
只包含非业务的有效必要字段。
(1)插入时间戳字段 insert_time, date类型。
由:ingest默认生成,不手动添加,提升效率。
(2)手机号字段 phone_number, text和keyword类型。
text类型基于ngram分词,主要方便phone_number全文检索。
keyword类型方便:排序和聚合使用。
(3)后8位手机号字段 last_eight_number, keyword类型。
只聚合和排序用,不检索。
ingest pipeline的核心功能能够理解为写入前数据的ETL。
而:insert_time能够自动生成、last_eight_number能够基于phone_number提取。
定义以下:
# 0.create ingest_pipeline of insert_time and last_eight_number PUT _ingest/pipeline/initialize { "description": "Adds insert_time timestamp to documents", "processors": [ { "set": { "field": "_source.insert_time", "value": "{{_ingest.timestamp}}" } }, { "script": { "lang": "painless", "source": "ctx.last_eight_number = (ctx.phone_number.substring(3,11))" } } ] }
两个索引:
索引1:phone_index,存储所有手机号(数千万)
索引2:phone_couple_index,存储情侣号
因为两索引Mapping结构同样,使用模板管理会更为方便。
定义以下:
# 1.create template of phone_index and phone_couple_index PUT _template/phone_template { "index_patterns": "phone_*", "settings": { "number_of_replicas": 0, "index.default_pipeline": "initialize", "index": { "max_ngram_diff": "13", "analysis": { "analyzer": { "ngram_analyzer": { "tokenizer": "ngram_tokenizer" } }, "tokenizer": { "ngram_tokenizer": { "token_chars": [ "letter", "digit" ], "min_gram": "1", "type": "ngram", "max_gram": "11" } } } } }, "mappings": { "properties": { "insert_time":{ "type":"date" }, "last_eight_number":{ "type":"keyword" }, "phone_number": { "type": "text", "fields": { "keyword": { "type": "keyword" } }, "analyzer": "ngram_analyzer" } } } }
PUT phone_index PUT phone_couple_index
采用模拟数据,实际业务会有所区别。
POST phone_index/_bulk {"index":{"_id":1}} {"phone_number" : "13511112222"} {"index":{"_id":2}} {"phone_number" : "13611112222"} {"index":{"_id":3}} {"phone_number" : "13711112222"} {"index":{"_id":4}} {"phone_number" : "13811112222"} {"index":{"_id":5}} {"phone_number" : "13844248474"} {"index":{"_id":6}} {"phone_number" : "13866113333"} {"index":{"_id":7}} {"phone_number" : "15766113333"}
模拟数据显示,有两组情侣号。
第一组情侣号尾数:“11112222”
第二组情侣号尾数:“66113333”
如前所述,聚合的目的是:提取出情侣号(>=2
)的手机号或对应id。
GET phone_index/_search { "size": 0, "query": { "range": { "insert_time": { "gte": 1584871200000, "lte": 1584892800000 } } }, "aggs": { "last_aggs": { "terms": { "field": "last_eight_number", "min_doc_count": 2, "size": 10, "shard_size": 30 }, "aggs": { "sub_top_hits_aggs": { "top_hits": { "size": 100, "_source": { "includes": "phone_number" }, "sort": [ { "phone_number.keyword": { "order": "asc" } } ] } } } } } }
注意:
查询的目的:按时间间隔取数据。缘由:「聚合全量性能太差」。
外层聚合last_aggs统计:情侣号分组及数量。
内层子聚合sub_top_hits_aggs统计:下钻的手机号或_id等信息。
min_doc_count做用:聚合后的分组记录最小条数,情侣号必须>=2,则设置为2。
基于3.3 取出的知足条件的id进行跨索引迁移。
POST _reindex { "source": { "index": "phone_index", "query": { "terms": { "_id": [ 1, 2, 3, 4, 6, 7 ] } } }, "dest": { "index": "phone_couple_index" } }
注意:实际业务须要考虑数据规模,划定轮询时间间隔区间。
建议:按照2.3章节的流程图执行。
第3节的实战一把实际是基于基础数据都写入ES了再作的处理。
核心的操做都是基于Elasticsearch完成的。
试想一下,这个环节若是提早是否是更合理呢?
数据图以下所示:
电话数据信息写入消息队列(如:kafka、rocketmq、rabbitmq等)。
消息队列能够直接同步到ES的phone_index索引。如:红线所示。
情侣号的处理借助第三方redis服务实现,逐条过滤,知足条件的数据同步到ES的情侣号索引phone_couple_index。如:绿线所示。
这样,Elasticsearch只干它最擅长的事情,剩下的工做前置交给消息队列完成。
本文就提出问题作了详细的阐述和实践,用到Elasticsearch 模板、Ingest、reindex等核心知识点和操做,给线上业务提供了理论参考。
你们对本文有异议或者有更好的方案,欢迎留言交流。
推荐阅读:
一、Elasticsearch的ETL利器——Ingest节点
二、干货 | Elasticsearch基础但很是有用的功能之二:模板
四、干货 | 95后运维小哥20天+经过Elastic认证考试经验分享
更短期更快习得更多干货!