自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

空間換時間-將查詢數(shù)據(jù)性能提升100倍的計數(shù)系統(tǒng)實踐

開發(fā)
對于企業(yè)和平臺而言,計數(shù)系統(tǒng)能夠提供一個有效的量化依據(jù),幫助優(yōu)化產(chǎn)品、制定營銷策略、提升用戶體驗,因此它成為了數(shù)據(jù)統(tǒng)計和用戶分析的核心工具。

1 背景

俠客匯的業(yè)務運營,根據(jù)目前公司的業(yè)務體量和運營方式,結合市場上對標競品的DAU數(shù)據(jù)分析,再借鑒國際上有很多會員制的自由交易市場玩法,決定建立一個B2B的二手同行自由交易平臺。通過提供擔保交易能力,讓所有交易能在平臺內(nèi)完成閉環(huán),平臺通過真實數(shù)據(jù)為商戶提供信息認證,打造具有公信力的背書。通過推薦和風控能力形成護城河,讓用戶留在平臺,實現(xiàn)合作共贏。

2 前言

在信息爆炸的時代,計數(shù)系統(tǒng)幾乎無處不在,從社交平臺上的點贊量、評論數(shù),到電商平臺的瀏覽量、訂單數(shù)量,再到內(nèi)容網(wǎng)站的訪問量統(tǒng)計,計數(shù)系統(tǒng)為各種業(yè)務提供了實時、準確的數(shù)據(jù)支持。這些數(shù)據(jù)不僅是簡單的數(shù)字,它們可以反映出用戶對內(nèi)容的興趣、商品的受歡迎程度、市場的需求變化,甚至可以用于預測未來趨勢。對于企業(yè)和平臺而言,計數(shù)系統(tǒng)能夠提供一個有效的量化依據(jù),幫助優(yōu)化產(chǎn)品、制定營銷策略、提升用戶體驗,因此它成為了數(shù)據(jù)統(tǒng)計和用戶分析的核心工具。

3 需要統(tǒng)計的計數(shù)維度

3.1 按照統(tǒng)計內(nèi)容維度劃分

  • 內(nèi)容維度:用戶發(fā)帖數(shù),帖子評論數(shù),帖子點贊數(shù),評論列表未讀總數(shù),關注列表未讀總數(shù),點贊列表未讀總數(shù),評論點贊數(shù),靠譜未讀計數(shù),同行認證未讀計數(shù),商品詳情瀏覽數(shù)量,商品負反饋數(shù)量。
  • 用戶維度:近期動態(tài)個數(shù),用戶關注數(shù),用戶粉絲數(shù)。
  • 交易維度:用戶回收單成交數(shù),用戶送檢數(shù),報價單計數(shù),用戶B2B訂單賣出計數(shù), 用戶B2B訂單買入計數(shù),用戶發(fā)布商品計數(shù),用戶已售商品計數(shù),用戶已購商品計數(shù)。

3.2 按照統(tǒng)計時間維度劃分

  • 實時維度:上述內(nèi)容維度的統(tǒng)計基本都是實時統(tǒng)計的維度。
  • 時間段維度:最近N天發(fā)布的商品數(shù),最近N天售賣商品的數(shù)量,最近N天發(fā)布的帖子數(shù)等等。

具體案例:

1.個人主頁會有我關注的數(shù)量,關注我的數(shù)量,靠譜數(shù)量,交易數(shù)量,以及一系列的按時間段統(tǒng)計的跑馬燈數(shù)據(jù)。多數(shù)用戶維度相關的數(shù)據(jù)都是在個人中心的上半部分進行展示,而下半部分,則是對這個用戶內(nèi)容維度,交易維度統(tǒng)計的一個匯總。同時,統(tǒng)計的數(shù)據(jù),既有實時維度,也會有時間段維度,例如,我們會統(tǒng)計用戶的總的交易單量,也會統(tǒng)計這個用戶最近N天的交易數(shù)量。

圖片

2.在首頁找同行頁面,顧名思義,找同行頁面就是用來尋找用戶的,所以這個頁面會重點展示這個用戶在用戶維度的數(shù)據(jù)統(tǒng)計,同行關注數(shù),交易人數(shù),靠譜數(shù)等。

圖片

3.在自由市場頁面,我們同樣會重點展示這個用戶在用戶維度的數(shù)據(jù)統(tǒng)計,但是與此同時,我們還會展示他的交易評分,方便有需要的用戶,能夠多維度的篩選自己的交易伙伴。

圖片

4.在消息通知頁面,我們則著重統(tǒng)計這個用戶需要關注的動態(tài)數(shù)據(jù),點贊數(shù),評論數(shù),新增關注數(shù)量等等。

圖片

4 為什么要做自己的計數(shù)系統(tǒng)

上面已經(jīng)陳述了我們所需要的統(tǒng)計的計數(shù)業(yè)務場景,以及不同的統(tǒng)計維度和統(tǒng)計方式。簡單的將我們需要的計數(shù)功能做一個總結:

  • 基礎功能:能夠實時計算需要統(tǒng)計數(shù)量的總數(shù),而且能夠批量查詢。
  • 拓展功能:能夠兼容不同的業(yè)務場景,而且能夠由業(yè)務方自己控制增減的數(shù)量。
  • 進階功能:能夠根據(jù)時間段進行查詢,而且時間段由業(yè)務方自由控制,支持不同的時間維度,分鐘,小時,天,周,月。

功能總結完成,從節(jié)省時間和資源的角度來說,公司的其他部門是不是已經(jīng)有對應的功能可以提供了呢?我們首先想到的是數(shù)據(jù)組,畢竟數(shù)據(jù)組在整理和統(tǒng)計數(shù)據(jù)這方面是專業(yè)的。其次我們想到的是中臺系統(tǒng),是不是有對應的功能可以直接提供。結果調(diào)研之后發(fā)現(xiàn),兩個部門現(xiàn)有的功能,并不能完全支持我們的需求。

  • 數(shù)據(jù)組方面,能夠支持我們不同的業(yè)務場景的統(tǒng)計,也可以支持我們的時間段查詢。但是,數(shù)據(jù)組目前的數(shù)據(jù)提供方式是T+1的模式,并不能夠對我們的業(yè)務場景進行實時的統(tǒng)計。也就是說,他滿足了我們的拓展功能和進階功能,但是在基礎功能方面的支持是有限的。
  • 中臺方面,計數(shù)系統(tǒng)的功能是有的,但是功能相對簡單,由中臺來分配計數(shù)場景,然后由業(yè)務同學來驅動這個計數(shù)的加減?;A功能,拓展功能全都支持,如果計數(shù)過期,還可以通過回源數(shù)據(jù)接口來取數(shù)據(jù)。但是對于根據(jù)時間段查詢,也就是我們需要的進階功能,并不支持。

基于以上調(diào)研結果,并沒有現(xiàn)有的功能能夠直接滿足我們所有的需求,我們只能自己來實現(xiàn)這個計數(shù)的功能。

5 計數(shù)系統(tǒng)的設計方案

既然已經(jīng)決定要自己來做,那么就要從業(yè)務實際需要的業(yè)務場景來設計我們需要實現(xiàn)的功能。計數(shù)系統(tǒng)總的來說,實現(xiàn)我們所需要的功能并不是什么難點,設計方案的選擇,主要還是看我們自己的側重點,如果是開發(fā)時間角度考慮,可以選能夠快速實現(xiàn)功能的短期方案。如果是從長遠角度考慮,想讓計數(shù)系統(tǒng)獨立承擔一個類似中臺的角色,那也可以將計數(shù)系統(tǒng)作為一個獨立的通用模塊進行開發(fā)。

5.1 計數(shù)內(nèi)置的架構設計

在前言中已經(jīng)說過,本次開發(fā)是臨危受命,時間很緊張,那么如果想要在有效的時間內(nèi)完成本次開發(fā),時間是不可忽略的客觀條件。那么,最快速的方案莫過于直接采用count數(shù)據(jù)表+緩存的方式,這種方式在體量較小時,成本低、性能高、絕對精準,但隨著統(tǒng)計數(shù)據(jù)的體量逐漸變大、微服務拆分越來越細之后,該方案就會越來越難以支撐業(yè)務。

count表方案,基本可以總結為:

  • 一次請求多次查詢,for循環(huán)進行
  • 一次請求多次查詢,多個計數(shù)的查詢
  • 一次查詢一個count,每個計數(shù)都是一個count語句

所以這種方案一定會造成以下這幾種問題:

  • 性能瓶頸:隨著數(shù)據(jù)體量的越來越大,再用count的方式去進行數(shù)據(jù)統(tǒng)計,性能會變得越來越差。
  • 穩(wěn)定性風險:如果業(yè)務統(tǒng)計規(guī)則變得越來越復雜,使用數(shù)據(jù)庫count的方式會使數(shù)據(jù)查詢語句越來越復雜,容易引發(fā)慢SQL從而導致數(shù)據(jù)庫不穩(wěn)定。而且將計數(shù)的業(yè)務層和緩存與核心的業(yè)務邏輯放在一起,如果統(tǒng)計數(shù)據(jù)出現(xiàn)了問題,會影響核心業(yè)務的使用,小的問題會變成大的問題。
  • 一致性問題:部分計數(shù)場景下是定時更新緩存的策略,緩存操作和MySQL操作無法在一個事務中完成,會產(chǎn)生不一致的問題,且在越頻繁變更的場景下差異值就會越大。

綜上所屬,短期的方案雖然短小精悍,但是后期的隱患比較大,維護成本會很高,不是合理的選擇方案。

5.2 計數(shù)外置的架構設計

結合俠客匯當前的業(yè)務現(xiàn)狀、體量以及考慮中長期體量增長的規(guī)劃,我們也調(diào)研了業(yè)內(nèi)比較常見的一些實現(xiàn)方案,最終決定單獨維護一套計數(shù)系統(tǒng)。由計數(shù)系統(tǒng)來統(tǒng)計俠客匯所有的計數(shù)邏輯,計數(shù)系統(tǒng)和具體的業(yè)務邏輯完全脫離,只負責統(tǒng)計各個業(yè)務場景的計數(shù),具體的流程圖如下:圖片

5.2.1 計數(shù)方案中字段的定義

  • 全局計數(shù)器:記錄一個業(yè)務key的總量值,比如內(nèi)容點贊總數(shù),由一個全局計數(shù)器記錄即可。邏輯結構為(業(yè)務類型+實體id):value值。
  • 計數(shù)流水:實時上報的計數(shù)流水,可以支持按照精確時間維度查詢功能。
  • 業(yè)務類型:業(yè)務類型確定計數(shù)的接入業(yè)務,如交易單總量計數(shù)、內(nèi)容點贊計數(shù),為不同的接入業(yè)務類型。
  • 計數(shù)實體:實體id確定計數(shù)的目標對象,比如交易單、社區(qū)發(fā)布內(nèi)容等為一個計數(shù)實體。實體id在同一業(yè)務類型內(nèi)保證唯一性,如交易單總量計數(shù)業(yè)務,交易單id不能重復。圖片

之所以設計了這幾個字段,是因為我們可以通過最小的代價來實現(xiàn)最通用的功能。

  • 全局計數(shù)器必不可少,這是整個計數(shù)系統(tǒng)的靈魂,也是我們最常用的數(shù)據(jù)統(tǒng)計功能。
  • 計數(shù)流水是為了保存原始的數(shù)據(jù),后期如果有任何數(shù)據(jù)遷移,或者數(shù)據(jù)丟失后需要回源數(shù)據(jù)的,計數(shù)流水都會最后的屏障。
  • 業(yè)務類型是為了兼容各個業(yè)務場景,如果沒有這個字段,那么每個業(yè)務場景我們都需要一個接口來進行統(tǒng)計,每個業(yè)務場景我們都需要一個表來保存數(shù)據(jù),對后續(xù)拓展極為不利,也不能體現(xiàn)計數(shù)系統(tǒng)的通用性。
  • 計數(shù)實體的作用有兩個,一方面如果沒有實體id,只有業(yè)務類型的話,后續(xù)數(shù)據(jù)如果出現(xiàn)問題需要進行查詢的時候,我們無從查起。另一方面,有了計數(shù)實體,我們才能做一些冪等相關的校驗操作。

以上字段,同樣是redis的key的重要組成,redis的存儲實體如下圖

圖片

接下來說下計數(shù)系統(tǒng)的具體功能,我們對外提供的接口如下圖:

public interface BizCountService {
    /**
     * 計數(shù)上報接口
     *
     * @param request
     * @return
     */
    ZZOpenScfBaseResult<String> reportCount(ReportCountRequest request);

    /**
     * 流水記錄處理-保存日期計數(shù)的情況
     *
     * @param msg
     * @return
     */
    boolean saveDateOpt(BizCountRecordMsg msg);

    /**
     * 流水記錄處理-更新日期計數(shù)的情況
     *
     * @param heroBizCountDate
     * @param msg
     * @return
     */
    boolean updateDateOpt(HeroBizCountDate heroBizCountDate, BizCountRecordMsg msg);

    /**
     * 計數(shù)總量查詢接口
     *
     * @param request
     * @return
     */
    ZZOpenScfBaseResult<Long> total(TotalRequest request);

    /**
     * 批量查詢總量
     *
     * @param request
     * @return
     */
    ZZOpenScfBaseResult<CountBatchResponse> countBatch(CountBatchRequest request);

    /**
     * 清零接口
     * 總量置為0,插入一條計減當前總量的記錄;不影響歷史記錄查詢
     *
     * @param request
     * @return 重置前的舊值
     */
    ZZOpenScfBaseResult<Long> clear(ClearRequest request);

    /**
     * 按時間范圍計數(shù)接口
     *
     * @param request
     * @return
     */
    ZZOpenScfBaseResult<Long> countTimeBetween(CountTimeBetweenRequest request);

    /**
     * 批量id按時間范圍計數(shù)接口
     *
     * @param request
     * @return
     */
    ZZOpenScfBaseResult<Map<Long, Long>> batchCountTimeBetween(CountTimeBetweenBatchRequest request);

    /**
     * 最近n天的計數(shù)統(tǒng)計接口
     *
     * @param request
     * @return
     */
    ZZOpenScfBaseResult<Long> countRecent(CountRecentRequest request);

    /**
     * 批量-最近n天的計數(shù)統(tǒng)計接口
     *
     * @param request
     * @return
     */
    ZZOpenScfBaseResult<Map<Long, Long>> batchCountRecent(CountRecentBatchRequest request);

    /**
     * 組合計數(shù)總量查詢接口
     *
     * @param request
     * @return
     */
    Long combineTotal(CombineTotalRequest request);
}

我們整體的業(yè)務統(tǒng)計維度如下:

public enum BizCountType {

    /**
     * 用戶發(fā)帖數(shù)
     */
    USER_POST_COUNT("userPostCount","用戶發(fā)帖數(shù)"),
    /**
     * 帖子評論數(shù)
     */
    POST_REMARK_COUNT("postRemarkCount", "帖子評論數(shù)"),
    /**
     * 帖子點贊數(shù)
     */
    POST_APPROVE_COUNT("postApproveCount", "帖子點贊數(shù)"),
    /**
     * 評論列表未讀總數(shù)
     */
    REMARK_UNREAD_COUNT("remarkUnreadCount", "評論列表未讀總數(shù)"),
    /**
     * 關注列表未讀總數(shù)
     */
    ATTENTION_UNREAD_COUNT("attentionUnreadCount", "關注列表未讀總數(shù)"),
    /**
     * 點贊列表未讀總數(shù)
     */
    APPROVE_UNREAD_COUNT("approveUnreadCount", "點贊列表未讀總數(shù)"),
    /**
     * 評論點贊數(shù)
     */
    REMARK_APPROVE_COUNT("remarkApproveCount", "評論點贊數(shù)"),
    /**
     * 靠譜數(shù)
     */
    RELIABLE_COUNT("reliableCount", "靠譜數(shù)"),
    /**
     * 不靠譜數(shù)
     */
    UNRELIABLE_COUNT("unreliableCount", "不靠譜數(shù)"),
    /**
     * 近期動態(tài)個數(shù)
     */
    RECENT_COUNT("recentCount", "近期動態(tài)個數(shù)"),
    /**
     * 用戶回收單成交數(shù)
     */
    USER_RECYCLE_ORDER_COUNT("userRecycleOrderCount", "用戶回收單成交數(shù)"),
    /**
     * 用戶關注數(shù)
     */
    USER_ATTENTION_COUNT("userAttentionCount", "用戶關注數(shù)"),

    /**
     * 用戶粉絲數(shù)
     */
    USER_FOLLOWER_COUNT("userFollowerCount", "用戶粉絲數(shù)"),
    /**
     * 用戶送檢數(shù)
     */
    USER_SUBMISSION_COUNT("userSubmissionCount", "用戶送檢數(shù)"),

    /**
     * 報價單計數(shù)
     */
    PRICE_SHEET_COUNT("priceSheetCount", "報價單計數(shù)"),

    /**
     * 靠譜未讀計數(shù)
     */
    RELIABLE_UNREAD_COUNT("reliableUnreadCount", "靠譜未讀計數(shù)"),
    /**
     * 同行認證未讀計數(shù)
     */
    FELLOW_CERTIFICATE_UNREAD_COUNT("fellowCertificateUnreadCount", "同行認證未讀計數(shù)"),

    /**
     * 用戶B2B訂單賣出數(shù)
     */
    USER_B2B_ORDER_SOLD_COUNT("b2bOrderSoldCount", "用戶B2B訂單賣出計數(shù)"),

    /**
     * 用戶B2B訂單買入數(shù)
     */
    USER_B2B_ORDER_PURCHASE_COUNT("b2bOrderPurchaseCount", "用戶B2B訂單買入計數(shù)"),

    /**
     * 用戶發(fā)布商品數(shù)
     */
    USER_COMMODITY_PUBLISH_COUNT("userProductPublishCount", "用戶發(fā)布商品計數(shù)"),

    /**
     * 用戶已售商品數(shù)
     */
    USER_COMMODITY_SOLD_COUNT("userProductSoldCount", "用戶已售商品計數(shù)"),

    /**
     * 用戶已購買商品數(shù)
     */
    USER_COMMODITY_PURCHASE_COUNT("userProductPurchaseCount", "用戶已購商品計數(shù)"),
    /**
     * 商品詳情瀏覽數(shù)量
     */
    COMMODITY_BROWSE_COUNT("commodityBrowseCount", "商品詳情瀏覽數(shù)量"),
    /**
     * 商品負反饋數(shù)量
     */
    COMMODITY_NEGATIVE_FEEDBACK_COUNT("commodityNegativeFeedbackCount", "商品負反饋數(shù)量"),
    ;
    /**
     * 業(yè)務類型
     */
    private String code;
    /**
     * 業(yè)務描述
     */
    private String desc;

    public static BizCountType getByCode(String code) {
        return EnumParser.parse(BizCountType.class, BizCountType::getCode, code);
    }
}

5.2.2 計數(shù)系統(tǒng)的上報流程

在整個上報的流程中,主要分為三個部分,獲取上報數(shù)據(jù),處理上報數(shù)據(jù),上報數(shù)據(jù)持久化。

獲取上報數(shù)據(jù)

數(shù)據(jù)的獲取一般有兩種方式,通過接口或通過MQ的方式,本次我們采取的是直接接口調(diào)用的方式進行處理。

之所以考慮直接用接口調(diào)用的方式進行處理,主要考慮一下幾個方面:

  • 實時性高:直接接口調(diào)用通常是同步的,調(diào)用方可以立即獲得響應。
  • 實現(xiàn)簡單:接口調(diào)用往往更容易實現(xiàn),不需要額外的消息隊列中間件配置和維護,減少了系統(tǒng)復雜度和運維成本。對于只需要簡單請求-響應模式的服務來說,接口調(diào)用通常是更簡單直接的選擇。
  • 調(diào)用順序明確:接口調(diào)用是同步的,天然保證了調(diào)用順序,特別適合一些需要嚴格順序的場景。而 MQ 消息可能會因為分區(qū)、并發(fā)消費等原因導致消息處理順序變化,不適用于需要嚴格順序的業(yè)務。
  • 性能開銷小:直接調(diào)用避免了消息的中間傳輸、序列化和反序列化的開銷,通常性能更優(yōu)。
  • 調(diào)試方便:接口調(diào)用的流程更清晰,調(diào)試和問題排查更簡單。使用 MQ 消息時,涉及消息的存儲、轉發(fā)、消費等多個環(huán)節(jié),排查問題時需要在消息隊列和消費端分別查看日志和狀態(tài),調(diào)試難度更大。

處理上報數(shù)據(jù)

每個接口,會有一些邏輯上的校驗,例如,業(yè)務類型和實體id不能為空,不做其他的業(yè)務邏輯的校驗,保持計數(shù)系統(tǒng)的通用性,避免業(yè)務的侵入。

上報數(shù)據(jù)持久化

持久化部分主要分為兩塊,一是DB持久化,二是對于緩存的更新。

我們整體的流程是,將數(shù)據(jù)庫的變更和redis的緩存放在同一個事務中,優(yōu)先更新數(shù)據(jù)庫,然后將計數(shù)流水發(fā)送mq消息,由另一個接口單獨進行流水統(tǒng)計,最后更新redis緩存,如果事務失敗的話,可以保證整體的一致性。至于數(shù)據(jù)的加減,由業(yè)務方來控制,加減的大小也由業(yè)務方來控制,我們只進行傻瓜式操作。具體代碼如下:

public ZZOpenScfBaseResult<String> reportCount(ReportCountRequest request) {
        boolean valid = checkAndProcessReportRequest(request);
        if(!valid){
            return ZZOpenScfBaseResult.buildErr(-1,"參數(shù)不合法");
        }

        //執(zhí)行插入、更新總數(shù)的邏輯
        boolean locked = redissionLockHelper.tryLockBizCountTotal(request.getEntityId(), request.getBizType(), () -> {
            heroBizCountTotalManager.saveOrUpdate(request.getEntityId(), request.getBizType(), request.getCount());
        });
        if(!locked){
            log.error("lock failed,request:{}", request);
            WxWarnTemplateUtil.warnOutService("計數(shù)上報-獲取鎖異常");
            return ZZOpenScfBaseResult.buildErr(-1,"獲取鎖失敗");
        }

        //發(fā)送消息
        try {
            bizCountRecordProducer.sendBizCountRecordMsg(buildRecordMsg(request));
        }catch (Exception e){
            WxWarnTemplateUtil.warnOutService("計數(shù)上報-發(fā)送消息異常");
            log.error("send report msg error, request:{}", request, e);
        }
        //同步總量至緩存,不影響最終一致性,且緩存有有效期,所以不阻塞流程
        try {
            syncTotal2Cache(request.getBizType(), request.getEntityId());
        }catch (Exception e){
            log.error("sync total from db error, request:{}", request, e);
            WxWarnTemplateUtil.warnOutService("計數(shù)上報-同步數(shù)據(jù)至緩存異常");
        }
        return ZZOpenScfBaseResult.buildSucc("");
    }

不得不說的技術設計細節(jié):以空間換時間

從以上代碼中可以看出,我們在整個存儲的過程中是發(fā)送了一條MQ消息,還記得我們之前提過,我們是有時間段維度的數(shù)據(jù)統(tǒng)計的,這個消息就是幫助我們縮短時間段查詢響應時間的關鍵,是真正實現(xiàn)了我們以空間換時間的地方。具體代碼邏輯如下:

public boolean bizCountRecord(String msgId, BizCountRecordMsg body) {
        log.info("bizCountRecord msgId={} body={}", msgId, GsonUtil.toJson(body));
        AtomicBoolean rst = new AtomicBoolean();
        boolean locked = redissionLockHelper.tryLockBizCountTotal(body.getEntityId(), body.getBizType(), () -> {
            HeroBizCountDate heroBizCountDate = heroBizCountDateManager.get(body.getEntityId(), body.getBizType().getCode(), body.getTimestamp());
            if(heroBizCountDate == null){
                rst.set(bizCountService.saveDateOpt(body));
            }
            else{
                rst.set(bizCountService.updateDateOpt(heroBizCountDate, body));
            }
        });
        if(!locked){
            log.info("bizCountRecord msgId={} body={} lock failed", msgId, GsonUtil.toJson(body));
            WxWarnTemplateUtil.warnOutService("計數(shù)消費-獲取鎖失敗");
            return false;
        }
        return rst.get();
    }

從以上代碼可以看出,我們在接受到了消息的同時,又單獨維護了一條以業(yè)務類型和實體id為組合key的,以天為維度的數(shù)據(jù)匯總表。有了這個數(shù)據(jù)表之后,我們就有了一條天然的時間維度。如果需要查詢N天的數(shù)據(jù),就不在需要count上報數(shù)據(jù)的流水表,可以直接通過當前的數(shù)據(jù)表,以天的問題來進行查詢。如果同一個業(yè)務類型和實體id,每天有1000的數(shù)據(jù)上報,在流水表中我們需要查詢3000條數(shù)據(jù),而在這個以天為維度的匯總表中,我們只需要查詢3條數(shù)據(jù)。這個比例會隨著上報計數(shù)數(shù)量級的增加,越來越大,讓我們的設計方案優(yōu)勢變得更加突出。

5.2.2 計數(shù)領域的讀取流程

  • 非時間段取數(shù)的讀取流程:整體邏輯比較簡潔,就是先查緩存,緩存不存在就查詢DB再寫入緩存即可。圖片
  • 有時間段取數(shù)的讀取流程:代碼如下圖所有,我們會先判斷一下,這個時間段內(nèi)是否有一個完成的自然日,如果沒有的話,直接查詢相關的流水表讀取數(shù)量。如果存在,先將時間段里面的自然日從我們按照天維度統(tǒng)計的匯總表里面讀取出來,然后其他的數(shù)據(jù)在從流水表中獲取,減少需要查詢的數(shù)量。
public Map<Long, Long> countTimeBetweenInternal(List<Long> entityIds, BizCountType bizType, Date start, Date end) {
        Map<Long, Long> totalMap = Maps.newHashMapWithExpectedSize(entityIds.size());
        if(!BizCommonDateUtils.containsWholeDays(start, end)){
            return heroBizCountRecordManager.computeRestTime(entityIds, bizType, start, end, Maps.newHashMap());
        }else{
            Map<Long, BizCountDateRange> dateRangeMap = heroBizCountDateManager.getDateRange(entityIds, bizType, start, end);
            Map<Long,Long> dateCountMap = heroBizCountDateManager.countDateBetween(entityIds, bizType, start, end);
            Map<Long,Long> restCountMap= heroBizCountRecordManager.computeRestTime(entityIds, bizType, start, end,dateRangeMap);
            entityIds.forEach(entityId -> {
                Long dateCount = dateCountMap.get(entityId);
                Long restCount = restCountMap.get(entityId);
                if(dateCount != null && restCount != null){
                    totalMap.put(entityId, dateCount + restCount);
                }else if(dateCount != null){
                    totalMap.put(entityId, dateCount);
                }else if(restCount != null){
                    totalMap.put(entityId, restCount);
                }
            });
        }
        return totalMap;
    }

6 總結與規(guī)劃

計數(shù)系統(tǒng)外置的架構設計也是業(yè)內(nèi)比較通用的設計方案。計數(shù)系統(tǒng)外置的架構設計和傳統(tǒng)的計數(shù)系統(tǒng)內(nèi)置的架構設計相比,它能夠顯著降低各業(yè)務在復雜計數(shù)場景下的維護成本,增強代碼功能的復用性與通用性,提高迭代效率并提升系統(tǒng)穩(wěn)定性。獨立出來后,一旦出現(xiàn)異常,業(yè)務可在短時間內(nèi)進行降級處理,進而減小對核心業(yè)務的影響范圍。此外,針對時間段查詢,采用以空間換時間的設計方式,能夠減少數(shù)據(jù)的查詢數(shù)量,從而提升查詢性能,縮短查詢時間。當然,我們本次受限于開發(fā)時間,也有一些不足之處:

1.時間范圍的查詢直接是DB查詢。目前的時間段查詢還是通過count表直接進行的查詢,不過目前時間段查詢數(shù)據(jù)統(tǒng)計需要用到的地方不多,暫時不會有性能方面的影響,后續(xù)可以通過持續(xù)迭代來進行改進。

2.沒有根據(jù)業(yè)務的使用場景來進行劃分。統(tǒng)計數(shù)據(jù)的使用也有讀多寫少的場景,使用緩存來保存讀多寫少的計數(shù),其實一致性要求不高的計數(shù),也可以先用緩存保存,然后定期刷到數(shù)據(jù)庫中,以降低數(shù)據(jù)庫的讀寫壓力。

責任編輯:龐桂玉 來源: 轉轉技術
相關推薦

2021-04-21 18:57:16

二進制存儲空間

2022-04-21 07:51:51

場景JavaSQL

2020-03-26 12:38:15

代碼節(jié)點數(shù)據(jù)

2022-08-12 22:53:32

HadoopHDFS分布式

2022-11-27 17:39:06

大數(shù)據(jù)集群性能

2025-01-10 11:42:13

2024-10-29 08:21:05

2011-04-18 11:27:49

空間時間數(shù)據(jù)庫設計

2014-04-01 09:52:46

MySQL

2013-09-26 14:11:23

SQL性能優(yōu)化

2017-05-11 11:30:43

MySQL查詢速度

2011-07-01 10:11:39

2024-07-17 08:25:44

2014-03-26 10:00:06

RailsRails性能

2011-08-16 09:05:21

SQL Server數(shù)測試索引空間換時間

2014-11-18 11:45:42

賽靈思

2023-10-20 08:12:00

JDK21線程池配置

2024-08-01 08:06:11

虛擬線程性能

2018-12-10 15:13:06

緩存系統(tǒng)性能數(shù)據(jù)

2020-07-21 15:40:55

NginxJava服務器
點贊
收藏

51CTO技術棧公眾號