国产v亚洲v天堂无码久久无码_久久久久综合精品福利啪啪_美女扒开尿口让男人桶_国产福利第一视频在线播放_滨崎步无码AⅤ一区二区三区_三年片免费观看了_大屁股妇女流出白浆_泷川苏菲亚无码AV_我想看我想看一级男同乱伦_国产精品午夜福利免费视频,gogo国模全球大胆高清摄影图,2008门艳照全集视频,欧美午夜在线精品品亚洲AV中文无码乱人伦在线播放

[轉(zhuǎn)]基于Solr的淘寶商家交易數(shù)據(jù)實(shí)時(shí)查詢(xún)方法
來(lái)源: 羅源/
廣州農(nóng)商銀行
3809
3
0
2016-02-02

基于Solr的淘寶商家交易數(shù)據(jù)實(shí)時(shí)查詢(xún)方法


摘要前言 DT時(shí)代對(duì)平臺(tái)或商家來(lái)說(shuō)最有價(jià)值的就是數(shù)據(jù)了,,在大數(shù)據(jù)時(shí)代數(shù)據(jù)呈現(xiàn)出數(shù)據(jù)量大,數(shù)據(jù)的維度多的特點(diǎn),用戶會(huì)使用多維度隨意組合條件快速召回?cái)?shù)據(jù),。數(shù)據(jù)處理業(yè)務(wù)場(chǎng)景需要實(shí)時(shí)性,,需要能夠快速精準(zhǔn)的獲得到需要的數(shù)據(jù),。之前的通過(guò)數(shù)據(jù)庫(kù)的方式來(lái)處理數(shù)據(jù)的方式,,由于數(shù)據(jù)庫(kù)的某些固有特性已經(jīng)很難滿足大數(shù)據(jù)時(shí)代對(duì)

前言

DT時(shí)代對(duì)平臺(tái)或商家來(lái)說(shuō)最有價(jià)值的就是數(shù)據(jù)了,,在大數(shù)據(jù)時(shí)代數(shù)據(jù)呈現(xiàn)出數(shù)據(jù)量大,,數(shù)據(jù)的維度多的特點(diǎn),,用戶會(huì)使用多維度隨意組合條件快速召回?cái)?shù)據(jù)。數(shù)據(jù)處理業(yè)務(wù)場(chǎng)景需要實(shí)時(shí)性,,需要能夠快速精準(zhǔn)的獲得到需要的數(shù)據(jù),。之前的通過(guò)數(shù)據(jù)庫(kù)的方式來(lái)處理數(shù)據(jù)的方式,由于數(shù)據(jù)庫(kù)的某些固有特性已經(jīng)很難滿足大數(shù)據(jù)時(shí)代對(duì)數(shù)據(jù)處理的需求,。

`

所以,,在大數(shù)據(jù)時(shí)代使用hadoop,hive,,spark,,作為處理離線大數(shù)據(jù)的補(bǔ)充手段已經(jīng)大行其道。
以上提到的這些數(shù)據(jù)處理手段,只能離線數(shù)據(jù)處理方式,,無(wú)法實(shí)現(xiàn)實(shí)時(shí)性,。Solr作為補(bǔ)充,能夠很好地解決大數(shù)據(jù)的多維度查詢(xún)和數(shù)據(jù)召回實(shí)時(shí)性要求,。

`

本文通過(guò)分析阿里淘寶聚石塔環(huán)境中遇到的一個(gè)具體需求是如何實(shí)現(xiàn)的,,通過(guò)這個(gè)例子,拋磚引玉來(lái)體現(xiàn)SORL在數(shù)據(jù)處理上的優(yōu)勢(shì),。

需求說(shuō)明

阿里聚石塔是銜接淘寶大賣(mài)家,,軟件開(kāi)發(fā)者和平臺(tái)提供者這三者的生態(tài)圈,阿里通過(guò)聚石塔平臺(tái),,將阿里云底層的PAAS,,IAAS環(huán)境提供給第三方開(kāi)發(fā)者,而第三方開(kāi)發(fā)者可以通過(guò)自己開(kāi)發(fā)的軟件產(chǎn)品,,比如ERP,,CRM系統(tǒng)販賣(mài)給淘寶上的大賣(mài)家,提高大賣(mài)家的工作效率,。

`

賣(mài)家的交易數(shù)據(jù)是最有價(jià)值的數(shù)據(jù),,通過(guò)交易數(shù)據(jù)可以衍生出很多產(chǎn)品,例如管理交易的ERP軟件,,會(huì)員營(yíng)銷(xiāo)工具CRM,,在聚石塔環(huán)境中通過(guò)大賣(mài)家授權(quán),這部分?jǐn)?shù)據(jù)可以授權(quán)給獨(dú)立軟件開(kāi)發(fā)者ISV,。

`

在CRM系統(tǒng)中需要能夠通過(guò)設(shè)置買(mǎi)家的行為屬性快速過(guò)濾出有價(jià)值的買(mǎi)家記錄,,進(jìn)行精準(zhǔn)會(huì)員營(yíng)銷(xiāo)。
以下是兩個(gè)具體需求,,首先看兩個(gè)線框圖:
image001

以上是賣(mài)家需要實(shí)時(shí)篩選一段時(shí)間內(nèi)購(gòu)買(mǎi)數(shù)量在一個(gè)區(qū)間之內(nèi)的買(mǎi)家,。

再看一個(gè)線框圖:
image002
賣(mài)家需要實(shí)時(shí)搜索一個(gè)時(shí)間段內(nèi),消費(fèi)金額在某個(gè)區(qū)間之內(nèi)的買(mǎi)家會(huì)員,。這里的區(qū)間是以天為單位的,,時(shí)間跨度可長(zhǎng)可短。

`

了解了線框圖之后,,我們還要再看看對(duì)應(yīng)的數(shù)據(jù)庫(kù)ER圖:
image003

`

表結(jié)構(gòu)相當(dāng)簡(jiǎn)單,,只有兩張表,,稍微有點(diǎn)經(jīng)驗(yàn)的開(kāi)發(fā)工程師就會(huì)寫(xiě)出以下SQL:

select buyer.buyer_id,count(trade.trade_id) as pay_countFrom buyer  inner join trade on(buyer.buyer_id = trade.buyer_id and buyer.seller_id = trade.seller_id)where trade.trade_time> ? and trade.trade_time < ? and buyer.seller_id=?group by buyer.buyer_idhaving pay_count > =1 AND pay_count <=5

第二個(gè)線框圖會(huì)用以下SQL語(yǔ)句來(lái)實(shí)現(xiàn):

select  buyer.buyer_id , sum(trade.fee) as pay_sumFrom buyer  inner join trade on( buyer.buyer_id = trade.buyer_id and buyer.seller_id = trade.seller_id)where trade.trade_time> ? AND trade.trade_time < ? and buyer.seller_id=?group by buyer.buyer_idhaving pay_sum > =20 and pay_sum <=100

以上,,兩個(gè)SQL語(yǔ)句大同小異,having部分稍有不同,, SQL語(yǔ)句并不算復(fù)雜,,但是在大數(shù)據(jù)情況下,無(wú)法在毫秒級(jí)反饋給用戶。另外,,假如where部分有其他查詢(xún)條件,,比如,買(mǎi)家的性別,,買(mǎi)家所屬的地區(qū)等,,就需要數(shù)據(jù)庫(kù)上設(shè)置更多的聯(lián)合索引,所以這個(gè)需求使用SQL語(yǔ)句根本無(wú)法實(shí)現(xiàn)的,。

查詢(xún)加速

問(wèn)題已經(jīng)明確,,那么解決的辦法是什么呢?是使用數(shù)據(jù)的存儲(chǔ)過(guò)程,?存儲(chǔ)過(guò)程底層還是依賴(lài)數(shù)據(jù)庫(kù)表的固有特性,,無(wú)非是提供一些以時(shí)間換空間的策略來(lái)實(shí)現(xiàn)罷了,換湯不換藥,,而且各個(gè)數(shù)據(jù)庫(kù)產(chǎn)品的存儲(chǔ)過(guò)程實(shí)現(xiàn)很很大差別,,一旦選擇了某一個(gè)數(shù)據(jù)的存儲(chǔ)過(guò)程之后以后再要遷移數(shù)據(jù)到其他數(shù)據(jù)平臺(tái)上就非常困難了。

`

這里要向大家隆重介紹搜索引擎Solr,。因?yàn)?,搜索引擎在底層使用倒排索引,這和數(shù)據(jù)庫(kù)有本質(zhì)區(qū)別,,倒排索引在數(shù)據(jù)查詢(xún)的性能上天生就比數(shù)據(jù)的Btree樹(shù)好上百倍,,具體原因不在這里展開(kāi)了。雖然某些數(shù)據(jù)庫(kù)也支持了倒排索引例如PG,,但畢竟不是通用的解決辦法,。一旦添加了這類(lèi)型的索引會(huì)影響數(shù)據(jù)的寫(xiě)入吞吐量,因?yàn)橹亟ㄋ饕浅:臅r(shí)間,。

`

開(kāi)源JAVA社區(qū)中使用最廣泛的應(yīng)該屬Solr了,,筆者所在的團(tuán)隊(duì)就是長(zhǎng)期研究將Solr應(yīng)用到企業(yè)級(jí)應(yīng)用場(chǎng)景中,在原生Solr之上做了很多優(yōu)化和適配,,方便企業(yè)級(jí)用戶使用,。

`

言歸正傳,先講講大致思路,,實(shí)現(xiàn)的架構(gòu)圖如下:
image006

全量數(shù)據(jù)準(zhǔn)備

這里要說(shuō)明的一點(diǎn),,發(fā)送到搜索引擎中的數(shù)據(jù)是一條寬表數(shù)據(jù),所謂寬表數(shù)據(jù)是將ER關(guān)系為1對(duì)N的實(shí)體,,聚合成一條記錄,。聚合方式有兩種,一種是向1的維度聚合,,比如用戶實(shí)體和消費(fèi)記錄實(shí)體,,寬表記錄如果是以用戶維度來(lái)聚合和話,,就會(huì)將所有的消費(fèi)記錄以某個(gè)特殊字符作為分割符,聚合成一個(gè)字段,,作為用戶記錄的一個(gè)冗余字段,。也可以以消費(fèi)記錄為維度聚合,將關(guān)聯(lián)的用戶信息作為一個(gè)冗余字段,,可想而知這樣的聚合方式用戶數(shù)據(jù)在索引數(shù)據(jù)中會(huì)有很多重復(fù),。

`

打?qū)挶磉@個(gè)環(huán)節(jié)看似和搜索不怎么相關(guān),但是合理的寬表數(shù)據(jù)結(jié)構(gòu)能大幅度地提高用戶數(shù)據(jù)查詢(xún)效率,。

`

全量流程用Hive來(lái)實(shí)現(xiàn)的,,如果是在阿里云公有云環(huán)境中可以用ODPS,因ODPS是PAAS服務(wù),。

`

增量通道,,需要寫(xiě)一個(gè)打?qū)挶聿僮鳌R驗(yàn)樗阉饕嫣赜械慕Y(jié)構(gòu),,增量同步更新持續(xù)一段時(shí)間之后會(huì)生成很多索引碎片,,所以必須要隔一段時(shí)間從數(shù)據(jù)源重新導(dǎo)出并構(gòu)建一次全量索引數(shù)據(jù)。

`

這里介紹一下上面提交到用戶-消費(fèi)記錄的寬表結(jié)構(gòu)(簡(jiǎn)單起見(jiàn),,去掉了表中和問(wèn)題域不相關(guān)的字段):

Buyer表:

買(mǎi)家id賣(mài)家id
Buyer_idSeller_id

Trade表:

買(mǎi)家id賣(mài)家id交易id交易時(shí)間單筆費(fèi)用
Buyer_idSeller_idtrade_idtrade_timeFee

聚合寬表結(jié)構(gòu):

買(mǎi)家id賣(mài)家iddynamic_info(聚合字段)
Buyer_idSeller_idsellerId_date_buyerId_payment_payCount[;sellerId_date_buyerId_payment_payCount]
`

這里需要對(duì)dynamic_info 聚合字段詳細(xì)說(shuō)明一下:

`

sellerId_date_buyerId_payment_payCount 這是一個(gè)聚合單元,,從左向右依次的含義是:賣(mài)家ID,購(gòu)買(mǎi)的日期(精確到天),,買(mǎi)家ID,,購(gòu)買(mǎi)天之內(nèi)的費(fèi)用總和,購(gòu)買(mǎi)天之內(nèi)的購(gòu)買(mǎi)次數(shù)總和,。

`

Dynamic_info字段可以有多個(gè)聚合單元組成,,每個(gè)單元中的date是按天去重的,假如一個(gè)用戶在某一天在一家店中有多條購(gòu)買(mǎi)記錄最終也會(huì)聚合成一個(gè)單元,。給一個(gè)聚合字段的實(shí)際示例:

`
Dynamic_info:9999_20151111_222_345.6_3;9999_20151212_222_627.5_1
`

這個(gè)字段的意義就是,,一個(gè)id為222的用戶在2015年雙11當(dāng)天購(gòu)買(mǎi)了3筆價(jià)值345元的商品,在雙12當(dāng)天在這個(gè)商家處又購(gòu)買(mǎi)了一筆價(jià)值627.5元的商品,。

`

之所以在Solr上進(jìn)行快速數(shù)據(jù)查詢(xún)的原因是,,Solr的數(shù)據(jù)源是一個(gè)已經(jīng)聚合好的一份數(shù)據(jù),數(shù)據(jù)庫(kù)上執(zhí)行的join操作會(huì)耗費(fèi)大量IO,,在Solr查詢(xún)省去了這部分時(shí)間,。

`

寬表數(shù)據(jù)從多個(gè)分表聚合,數(shù)據(jù)的語(yǔ)義沒(méi)有變化,,只是組織形式發(fā)生了變化,,如果一個(gè)SAAS的服務(wù)提供上同時(shí)為十幾萬(wàn)個(gè)大賣(mài)家提供篩選服務(wù),而每個(gè)大賣(mài)家又積累的交易數(shù)據(jù)是非常大的,,全部加在一起,,要將數(shù)據(jù)進(jìn)行聚合化操作,有非常大的CPU和IO開(kāi)銷(xiāo),,好在在云服務(wù)時(shí)代有強(qiáng)大的離線計(jì)算工具如hadoop,,ODPS可以將大數(shù)據(jù)如同肉面粉一般揉(處理)成任何你想要的結(jié)構(gòu),分分鐘不在話下,。

Solr引擎端數(shù)據(jù)處理

準(zhǔn)備好全量源數(shù)據(jù),,之后就是將其轉(zhuǎn)化為L(zhǎng)ucene的索引文件了,這個(gè)過(guò)程請(qǐng)查閱Solr Wiki便可,,這里不進(jìn)行闡述,。這里要重點(diǎn)描述的是Solr服務(wù)端如何響應(yīng)用戶的查詢(xún)請(qǐng)求,返回給用戶需要的查詢(xún)結(jié)果,。

`

處理用戶在時(shí)間段內(nèi)購(gòu)買(mǎi)量或購(gòu)買(mǎi)額度進(jìn)行過(guò)濾,,需要構(gòu)建一個(gè)QParser的插件,這個(gè)插件的作用是遍歷和查參數(shù)中匹配的條件項(xiàng)生成命中的DocSet命中結(jié)果集,。

Qparser代碼實(shí)現(xiàn)

下面是QparserPlugin.java節(jié)選:

for (LeafReaderContext leaf : readerContext.leaves()) {                 docBase = leaf.docBase;                 reader = leaf.reader();                 liveDocs = reader.getLiveDocs();                 terms = reader.terms("dynamic_info");                 termEnum = terms.iterator();                String prefixStart = sellerId + "_" + startTime;                String prefixEnd = sellerId + "_" + endTime;                String termStr = null;                int docid = -1;                if ((termEnum.seekCeil(new BytesRef(prefixStart))) != SeekStatus.END) {                    do {                         Matcher matcher = DYNAMIC_INFO                                 .matcher(termStr = termEnum.term()                                         .utf8ToString());                        if (!matcher.matches()) {                            continue;                         }                         posting = termEnum.postings(posting);                         docid = posting.nextDoc();if (!(docid != PostingsEnum.NO_MORE_DOCS     && (liveDocs == null || (liveDocs != null && liveDocs.get(docid))))) {        continue; }                        if ((matcher.group(1) + "_" + matcher.group(2))                                 .compareTo(prefixEnd) > 0) {                            break;                         }                         addStatis(buyerStatis, docBase, docid, matcher);                     } while (termEnum.next() != null);                 }             }

以上代碼的執(zhí)行邏輯是,,截取prefixStart和prefixEnd之間的term序列,進(jìn)行分析如果符合過(guò)濾條件就將對(duì)應(yīng)docid插入buyerStatis收集器中,。

`

等第一輪數(shù)據(jù)處理過(guò)程中就在對(duì)聚合結(jié)果進(jìn)行增量累加,,代碼如下:

private static StaticReduce addStatis(             Map<Integer, StaticReduce> buyerStatis, int docBase, int docid,             Matcher matcher) {         StaticReduce statis = buyerStatis.get(docBase + docid);        if (statis == null) {             statis = new StaticReduce(docBase + docid, Long.parseLong(matcher                     .group(3))/* buyerid */);             buyerStatis.put(docBase + docid, statis);         }        if (statis.buyerId != Long.parseLong(matcher.group(3))) {            return statis;         }        try {             statis.addPayCount(Integer.parseInt(matcher.group(5)));         } catch (Exception e) {         }        try {             statis.addPayment(Float.parseFloat(matcher.group(4)));         } catch (Exception e) {         }        return statis;     }

同時(shí)對(duì)購(gòu)買(mǎi)數(shù)量,和購(gòu)買(mǎi)金額進(jìn)行累加,。

`

最后對(duì)累加結(jié)果進(jìn)行過(guò)濾,,符合過(guò)濾條件的,將docid插入到bitset中:

for (StaticReduce statis : buyerStatis.values()) {                 // TODO 這里自己判斷是否要收集這條記錄        if (statis.payCount > Integer.MAX_VALUE                 || statis.paymentSum > 1) {            System.out.println("count:" + statis.payCount + ",sum:"                         + statis.paymentSum);                     bitSet.set(statis.luceneDocId);         } }BitDocIdSet docIdSet = new BitDocIdSet(bitSet);            DocIdSetIterator it = docIdSet.iterator();             final BitQuery bitquery = new BitQuery(it);            return new QParser(qstr, localParams, params, req) {                 @Override                 public Query parse() throws SyntaxError {                    return bitquery;                 }             };

最后將bitSet包裝成BitQuery作為Qparser的parse函數(shù)的返回值,,返回有solr進(jìn)一步和其他結(jié)果集進(jìn)行過(guò)濾,。

`

Solrconfig配置實(shí)現(xiàn)

需要將以上的QparserPlugin插件注入到solr中,需要在solrconfig中寫(xiě)以下配置:

  <queryParser name="timesegstats" class ="com.xxx.qp.TimeSegStatsQParserPlugin" >                                         <str name="buyerField">buyer_id</str>                                                                                                                     <str name="compoundField">dynamic_info </str>                                                                                 <str name="countField">emailSendCount</str>                                                                                              <str name="statsFields"></str>                                                                                                                     </queryParser>

Solr查詢(xún)語(yǔ)句Q參數(shù)設(shè)置:

q={!multiqp q.op=AND}seller_id:1441097932588 AND {!timesegstats sellerId=1441097932588 statsField=buyActivity startTime=20150901 endTime=20150924 startValue=1 endValue=200} AND {!timesegstats sellerId=1441097932588 statsField=paycount startTime=20140901 endTime=20150924 startValue=2 endValue=100}

總結(jié)

以上是一個(gè)用Solr搜索引擎解決數(shù)據(jù)庫(kù)查詢(xún)瓶頸的實(shí)例,,其實(shí)搜索引擎的使用場(chǎng)景非常廣泛,,不僅可以用在像百度這樣的大規(guī)模非結(jié)構(gòu)化的數(shù)據(jù)查詢(xún),可以定制比較復(fù)雜的排序規(guī)則,。Solr更可以解決像本文講到的數(shù)據(jù)庫(kù)加速的場(chǎng)景,,使得原本在數(shù)據(jù)庫(kù)上沒(méi)有無(wú)法實(shí)現(xiàn)的SQL查詢(xún),可以通過(guò)Solr搜索引擎上輕松實(shí)現(xiàn),。

`

本文講到的需求,,也可以使用像hive這樣的離線處理工具來(lái)實(shí)現(xiàn),每次處理完成后將結(jié)果再導(dǎo)入到mysql中,,業(yè)務(wù)端通過(guò)讀取數(shù)據(jù)庫(kù)表中的數(shù)據(jù)來(lái)向用戶展示處理結(jié)果,。這樣做雖然可行,但是,,沒(méi)有辦法將處理結(jié)果的實(shí)時(shí)性沒(méi)有辦法保證,,而且,,離線處理結(jié)果的數(shù)據(jù)結(jié)構(gòu)是固化的,沒(méi)有辦法做到將處理結(jié)果靈活調(diào)整,。而用Solr做到數(shù)據(jù)的查詢(xún)出口,,可以很好地解決以上兩個(gè)問(wèn)題。



登錄用戶可以查看和發(fā)表評(píng)論,, 請(qǐng)前往  登錄 或  注冊(cè),。
SCHOLAT.com 學(xué)者網(wǎng)
免責(zé)聲明 | 關(guān)于我們 | 聯(lián)系我們
聯(lián)系我們: