1、問題描述 您好,請教個問題。我現(xiàn)在有2千多萬的手機(jī)號碼信息保存在es里。5個分片,3個節(jié)點(diǎn)。 現(xiàn)在的需求是將后八位相同的號碼匹配到一起,重新放到一個index里。組成情侶號。方便后續(xù)查詢情侶號列表。 我目前的做法是用scroll查詢出一萬條,多線程循環(huán)一萬條中的每條,去全庫掃描---但是這種做法一分鐘才能處理一萬條。您有什么新的思路沒。 死磕Elasticsearch知識星球 https://t.zsxq.com/Iie66qV
問題補(bǔ)充:索引存儲了手機(jī)號,同時存儲了插入時間。 2、問題分析2.1 情侶號的定義后八位相同的號碼即為情侶號。 舉例: 13011112222 13511112222 13711112222
2.2 如何對后8位建立索引,以方便后續(xù)的識別?方案一 不單獨(dú)建索引,用script來實(shí)現(xiàn)缺點(diǎn):script效率低一些 方案二:寫入數(shù)據(jù)的時候,同時基于后八位創(chuàng)建新的字段。2.3 8位相同的號碼匹配到一起,重新放到一個index里怎么實(shí)現(xiàn)?Elasticsearch自帶reindex 功能就是實(shí)現(xiàn)索引遷移的,當(dāng)然自定義讀寫也可以實(shí)現(xiàn)。 方案一:遍歷方式+寫入。步驟 1:基于時間遞增循環(huán)遍歷,以起始的手機(jī)號為種子數(shù)據(jù),滿足后八位相同的加上標(biāo)記flag=1。 步驟 2:循環(huán)步驟1,滿足flag=1直接跳過,直到所有手機(jī)號遍歷一遍。 步驟 3:將包含flag=1的字段,reindex到情侶號索引。
方案二:聚合出情侶號組,將聚合結(jié)果reindex到情侶號索引。考慮到數(shù)據(jù)量級千萬級別,全量聚合不現(xiàn)實(shí)。 可以,基于時間切片,取出最小時間戳、最大時間戳,根據(jù)數(shù)據(jù)總量和時間范圍劃分出時間間隔。 舉例:以30分鐘為單位切割千萬級數(shù)據(jù)。  terms聚合只返回對應(yīng):key,value值,默認(rèn)value值由高到低排序。 key:代表手機(jī)號后8位,value:代表相同后8位的數(shù)據(jù)量。 步驟 2:top_hits子聚合取出手機(jī)號詳情。 步驟 3:json解析識別出步驟2的所有手機(jī)號或_id。 步驟 4:reindex步驟3的_id數(shù)據(jù)到情侶號索引。 步驟 5:時間切片周期遞增,直到所有數(shù)據(jù)遍歷完畢。
2.4 擴(kuò)展自問:手機(jī)號怎么存,才能查出來后8位?舉例:查詢“11112222”,返回2.1列表的三個手機(jī)號。 優(yōu)點(diǎn):無需額外字段存儲。 缺點(diǎn):效率低。 優(yōu)點(diǎn):效率高。 缺點(diǎn):需要獨(dú)立存儲的后8位字段。 3、實(shí)戰(zhàn)一把3.1 數(shù)據(jù)建模3.1.1 字段設(shè)計只包含非業(yè)務(wù)的有效必要字段。 (1)插入時間戳字段 insert_time, date類型。 由:ingest默認(rèn)生成,不手動添加,提高效率。 (2)手機(jī)號字段 phone_number, text和keyword類型。 text類型基于ngram分詞,主要方便phone_number全文檢索。
(3)后8位手機(jī)號字段 last_eight_number, keyword類型。 只聚合和排序用,不檢索。 3.1.2 ingest處理初始化數(shù)據(jù)先行ingest pipeline的核心功能可以理解為寫入前數(shù)據(jù)的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))" } } ] }
3.1.3 模板定義兩個索引: 由于兩索引Mapping結(jié)構(gòu)一樣,使用模板管理會更為方便。 定義如下: # 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" } } } }
3.1.4 索引定義PUT phone_index PUT phone_couple_index
3.2 數(shù)據(jù)寫入采用模擬數(shù)據(jù),實(shí)際業(yè)務(wù)會有所區(qū)別。 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"}
模擬數(shù)據(jù)顯示,有兩組情侶號。 第一組情侶號尾數(shù):“11112222” 第二組情侶號尾數(shù):“66113333”
3.2 數(shù)據(jù)聚合如前所述,聚合的目的是:提取出情侶號(>=2 )的手機(jī)號或?qū)?yīng)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" } } ] } } } } } }
注意: 查詢的目的:按時間間隔取數(shù)據(jù)。原因:「聚合全量性能太差」。 外層聚合last_aggs統(tǒng)計:情侶號分組及數(shù)量。 內(nèi)層子聚合sub_top_hits_aggs統(tǒng)計:下鉆的手機(jī)號或_id等信息。 min_doc_count作用:聚合后的分組記錄最小條數(shù),情侶號必須>=2,則設(shè)置為2。
3.4 數(shù)據(jù)遷移基于3.3 取出的滿足條件的id進(jìn)行跨索引遷移。 POST _reindex { "source": { "index": "phone_index", "query": { "terms": { "_id": [ 1, 2, 3, 4, 6, 7 ] } } }, "dest": { "index": "phone_couple_index" } }
注意:實(shí)際業(yè)務(wù)需要考慮數(shù)據(jù)規(guī)模,劃定輪詢時間間隔區(qū)間。 建議:按照2.3章節(jié)的流程圖執(zhí)行。 4、方案進(jìn)一步探究第3節(jié)的實(shí)戰(zhàn)一把實(shí)際是基于基礎(chǔ)數(shù)據(jù)都寫入ES了再做的處理。 核心的操作都是基于Elasticsearch完成的。 試想一下,這個環(huán)節(jié)如果提前是不是更合理呢? 數(shù)據(jù)圖如下所示:  電話數(shù)據(jù)信息寫入消息隊列(如:kafka、rocketmq、rabbitmq等)。 消息隊列可以直接同步到ES的phone_index索引。如:紅線所示。 情侶號的處理借助第三方redis服務(wù)實(shí)現(xiàn),逐條過濾,滿足條件的數(shù)據(jù)同步到ES的情侶號索引phone_couple_index。如:綠線所示。
這樣,Elasticsearch只干它最擅長的事情,剩下的工作前置交給消息隊列完成。 5、小結(jié)本文就提出問題做了詳細(xì)的闡述和實(shí)踐,用到Elasticsearch 模板、Ingest、reindex等核心知識點(diǎn)和操作,給線上業(yè)務(wù)提供了理論參考。 大家對本文有異議或者有更好的方案,歡迎留言交流。 |