<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          千萬(wàn)級(jí)數(shù)據(jù)查詢:CK、ES、RediSearch 誰(shuí)才是王炸?

          共 9564字,需瀏覽 20分鐘

           ·

          2023-01-05 08:45

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨 及時(shí)送達(dá) ??

          79874bb0eeb7067d1e8c015ceb5474f3.webp來(lái)源:c1n.cn/EmgJv

          • 前言
          • 初版設(shè)計(jì)方案
          • CK 分頁(yè)
          • 使用 ES Scroll Scan 優(yōu)化深翻頁(yè)
            • ES 的翻頁(yè)方案
            • 耗時(shí)數(shù)據(jù)
          • ES+Hbase 組合查詢方案
            • ES 查詢的兩個(gè)階段
            • 組合使用 Hbase
          • RediSearch+RedisJSON 優(yōu)化方案
            • RediSearch 性能數(shù)據(jù)
            • RedisJSON 性能數(shù)據(jù)
          • 總結(jié)

          前言

          在開(kāi)發(fā)中遇到一個(gè)業(yè)務(wù)訴求,需要在千萬(wàn)量級(jí)的底池?cái)?shù)據(jù)中篩選出不超過(guò) 10W 的數(shù)據(jù),并根據(jù)配置的權(quán)重規(guī)則進(jìn)行排序、打散(如同一個(gè)類目下的商品數(shù)據(jù)不能連續(xù)出現(xiàn) 3 次)。下面對(duì)該業(yè)務(wù)訴求的實(shí)現(xiàn),設(shè)計(jì)思路和方案優(yōu)化進(jìn)行介紹。

          對(duì)“千萬(wàn)量級(jí)數(shù)據(jù)中查詢 10W 量級(jí)的數(shù)據(jù)”設(shè)計(jì)了如下方案:

          • 多線程+CK 翻頁(yè)方案
          • ES scroll scan 深翻頁(yè)方案
          • ES+Hbase 組合方案
          • RediSearch+RedisJSON 組合方案

          初版設(shè)計(jì)方案

          整體方案設(shè)計(jì)為:

          • 先根據(jù)配置的「篩選規(guī)則」,從底池表中篩選出「目標(biāo)數(shù)據(jù)」
          • 在根據(jù)配置的「排序規(guī)則」,對(duì)「目標(biāo)數(shù)據(jù)」進(jìn)行排序,得到「結(jié)果數(shù)據(jù)」

          技術(shù)方案如下:

          每天運(yùn)行導(dǎo)數(shù)任務(wù),把現(xiàn)有的千萬(wàn)量級(jí)的底池?cái)?shù)據(jù)(Hive 表)導(dǎo)入到 Clickhouse 中,后續(xù)使用 CK 表進(jìn)行數(shù)據(jù)篩選。

          將業(yè)務(wù)配置的篩選規(guī)則和排序規(guī)則,構(gòu)建為一個(gè)「篩選 + 排序」對(duì)象 SelectionQueryCondition。

          從 CK 底池表取「目標(biāo)數(shù)據(jù)」時(shí),開(kāi)啟多線程,進(jìn)行分頁(yè)篩選,將獲取到的「目標(biāo)數(shù)據(jù)」存放到 result 列表中。

                
                //分頁(yè)大小??默認(rèn)?5000
          int?pageSize?=?this.getPageSize();
          //頁(yè)碼數(shù)
          int?pageCnt?=?totalNum?/?this.getPageSize()?+?1;

          List<Map<String,?Object>>?result?=?Lists.newArrayList();
          List<Future<List<Map<String,?Object>>>>?futureList?=?new?ArrayList<>(pageCnt);

          //開(kāi)啟多線程調(diào)用
          for?(int?i?=?1;?i?<=?pageCnt;?i++)?{
          ????//將業(yè)務(wù)配置的篩選規(guī)則和排序規(guī)則?構(gòu)建為?SelectionQueryCondition?對(duì)象
          ????SelectionQueryCondition?selectionQueryCondition?=?buildSelectionQueryCondition(selectionQueryRuleData);
          ????selectionQueryCondition.setPageSize(pageSize);
          ????selectionQueryCondition.setPage(i);
          ????futureList.add(selectionQueryEventPool.submit(new?QuerySelectionDataThread(selectionQueryCondition)));
          }


          for?(Future<List<Map<String,?Object>>>?future?:?futureList)?{
          ????//RPC?調(diào)用
          ????List<Map<String,?Object>>?queryRes?=?future.get(20,?TimeUnit.SECONDS);
          ????if?(CollectionUtils.isNotEmpty(queryRes))?{
          ????????//?將目標(biāo)數(shù)據(jù)存放在?result?中
          ????????result.addAll(queryRes);
          ????}
          }

          ④對(duì)目標(biāo)數(shù)據(jù) result 進(jìn)行排序,得到最終的「結(jié)果數(shù)據(jù)」。

          CK 分頁(yè)查詢

          在「初版設(shè)計(jì)方案」章節(jié)的第 3 步提到了「從 CK 底池表取目標(biāo)數(shù)據(jù)時(shí),開(kāi)啟多線程,進(jìn)行分頁(yè)篩選」。此處對(duì) CK 分頁(yè)查詢進(jìn)行介紹。

          ①封裝了 queryPoolSkuList 方法,負(fù)責(zé)從 CK 表中獲得目標(biāo)數(shù)據(jù)。該方法內(nèi)部調(diào)用了 sqlSession.selectList 方法。

                
                public?List<Map<String,?Object>>?queryPoolSkuList(?Map<String,?Object>?params?)?{
          ????List<Map<String,?Object>>?resultMaps?=?new?ArrayList<>();

          ????QueryCondition?queryCondition?=?parseQueryCondition(params);
          ????List<Map<String,?Object>>?mapList?=?lianNuDao.queryPoolSkuList(getCkDt(),queryCondition);
          ????if?(CollectionUtils.isNotEmpty(mapList))?{
          ????????for?(Map<String,Object>?data?:?mapList)?{
          ????????????resultMaps.add(camelKey(data));
          ????????}
          ????}
          ????return?resultMaps;
          }
          //?lianNuDao.queryPoolSkuList

          @Autowired
          @Qualifier("ckSqlNewSession")
          private?SqlSession?sqlSession;

          public?List<Map<String,?Object>>?queryPoolSkuList(?String?dt,?QueryCondition?queryCondition?)?{
          ????queryCondition.setDt(dt);
          ????queryCondition.checkMultiQueryItems();
          ????return?sqlSession.selectList("LianNu.queryPoolSkuList",queryCondition);
          }

          ②sqlSession.selectList 方法中調(diào)用了和 CK 交互的 queryPoolSkuList 查詢方法,部分代碼如下:

                
                <select?id="queryPoolSkuList"?parameterType="com.jd.bigai.domain.liannu.QueryCondition"?resultType="java.util.Map">
          ????select?sku_pool_id,i
          ????tem_sku_id,
          ????skuPoolName,
          ????price,
          ????...
          ????...
          ????businessType
          ????from?liannu_sku_pool_indicator_all
          ????where
          ????dt=#{dt}
          ????and
          ????<foreach?collection="queryItems"?separator="?and?"?item="queryItem"?open="?"?close="?"?>
          ????????<choose>
          ????????????<when?test="queryItem.type?==?'equal'">
          ????????????????${queryItem.field}?=?#{queryItem.value}
          ????????????</when>
          ????????????...
          ????????????...
          ????????</choose>
          ????</foreach>
          ????<if?test="orderBy?==?null">
          ????????group?by?sku_pool_id,item_sku_id
          ????</if>
          ????<if?test="orderBy?!=?null">
          ????????group?by?sku_pool_id,item_sku_id,${orderBy}?order?by?${orderBy}?${orderAd}
          ????</if>
          ????<if?test="limitEnd?!=?0">
          ????????limit?#{limitStart},#{limitEnd}
          ????</if>
          </select>

          ③可以看到,在 CK 分頁(yè)查詢時(shí),是通過(guò) limit #{limitStart},#{limitEnd} 實(shí)現(xiàn)的分頁(yè)。

          limit 分頁(yè)方案,在「深翻頁(yè)」時(shí)會(huì)存在性能問(wèn)題。初版方案上線后,在 1000W 量級(jí)的底池?cái)?shù)據(jù)中篩選 10W 的數(shù)據(jù),最壞耗時(shí)會(huì)達(dá)到 10s~18s 左右。

          使用 ES Scroll Scan 優(yōu)化深翻頁(yè)

          對(duì)于 CK 深翻頁(yè)時(shí)候的性能問(wèn)題,進(jìn)行了優(yōu)化,使用 Elasticsearch 的 scroll scan 翻頁(yè)方案進(jìn)行優(yōu)化。

          ES 的翻頁(yè)方案

          ES 翻頁(yè),有下面幾種方案:

          • from + size 翻頁(yè)
          • scroll 翻頁(yè)
          • scroll scan 翻頁(yè)
          • search after 翻頁(yè)

          421ca11b29b358317de218bba8bb7b9e.webp

          圖片

          對(duì)上述幾種翻頁(yè)方案,查詢不同數(shù)目的數(shù)據(jù),耗時(shí)數(shù)據(jù)如下表:

          e558cae0f493bd9e071b38b228b524e5.webp

          圖片

          耗時(shí)數(shù)據(jù)

          此處,分別使用 Elasticsearch 的 scroll scan 翻頁(yè)方案、初版中的 CK 翻頁(yè)方案進(jìn)行數(shù)據(jù)查詢,對(duì)比其耗時(shí)數(shù)據(jù)。

          a3cbb50fb1ff18f458df4b9e0b09376b.webp

          圖片

          ddd0264f9c231c8d8df60eeb5a2310bf.webp

          圖片

          如上測(cè)試數(shù)據(jù),可以發(fā)現(xiàn),以十萬(wàn),百萬(wàn),千萬(wàn)量級(jí)的底池為例:

          • 底池量級(jí)越大,查詢相同的數(shù)據(jù)量,耗時(shí)越大
          • 查詢結(jié)果 3W 以下時(shí),ES 性能優(yōu);查詢結(jié)果 5W 以上時(shí),CK 多線程性能優(yōu)

          ES+Hbase 組合查詢方案

          在「使用 ES Scroll Scan 優(yōu)化深翻頁(yè)」中,使用 Elasticsearch 的 scroll scan 翻頁(yè)方案對(duì)深翻頁(yè)問(wèn)題進(jìn)行了優(yōu)化,但在實(shí)現(xiàn)時(shí)為單線程調(diào)用,所以最終測(cè)試耗時(shí)數(shù)據(jù)并不是特別理想,和 CK 翻頁(yè)方案性能差不多。

          在調(diào)研階段發(fā)現(xiàn),從底池中取出 10W 的目標(biāo)數(shù)據(jù)時(shí),一個(gè)商品包含多個(gè)字段的信息(CK 表中一行記錄有 150 個(gè)字段信息),如價(jià)格、會(huì)員價(jià)、學(xué)生價(jià)、庫(kù)存、好評(píng)率等。

          對(duì)于一行記錄,當(dāng)減少獲取字段的個(gè)數(shù)時(shí),查詢耗時(shí)會(huì)有明顯下降。如對(duì) sku1的商品,從之前獲取價(jià)格、會(huì)員價(jià)、學(xué)生價(jià)、親友價(jià)、庫(kù)存等 100 個(gè)字段信息,縮減到只獲取價(jià)格、庫(kù)存這兩個(gè)字段信息。

          如下圖所示,使用 ES 查詢方案,對(duì)查詢同樣條數(shù)的場(chǎng)景(從千萬(wàn)級(jí)底池中篩選出 7W+ 條數(shù)據(jù)),獲取的每條記錄的字段個(gè)數(shù)從 32 縮減到 17,再縮減到 1個(gè)(其實(shí)是兩個(gè)字段,一個(gè)是商品唯一標(biāo)識(shí) sku_id,另一個(gè)是 ES 對(duì)每條文檔記錄的 doc_id)時(shí),查詢的耗時(shí)會(huì)從 9.3s 下降到 4.2s,再下降到 2.4s。

          5e90085f909ad438243061128cd814f4.webp

          圖片

          從中可以得出如下結(jié)論:

          • 一次 ES 查詢中,若查詢字段和信息較多,fetch 階段的耗時(shí),遠(yuǎn)大于 query 階段的耗時(shí)。
          • 一次 ES 查詢中,若查詢字段和信息較多,通過(guò)減少不必要的查詢字段,可以顯著縮短查詢耗時(shí)。

          下面對(duì)結(jié)論中涉及的 query 和 fetch 查詢階段進(jìn)行補(bǔ)充說(shuō)明。

          ES 查詢的兩個(gè)階段

          在 ES 中,搜索一般包括兩個(gè)階段:

          • query 階段: 根據(jù)查詢條件,確定要取哪些文檔(doc),篩選出文檔 ID(doc_id)
          • fetch 階段: 根據(jù) query 階段返回的文檔 ID(doc_id),取出具體的文檔(doc)

          組合使用 Hbase

          在《ES 億級(jí)數(shù)據(jù)檢索優(yōu)化,三秒返回突破性能瓶頸》一文調(diào)研的基礎(chǔ)上,發(fā)現(xiàn)「減少不必要的查詢展示字段」可以明顯縮短查詢耗時(shí)。

          沿著這個(gè)優(yōu)化思路,設(shè)計(jì)了一種新的查詢方案:

          • ES 僅用于條件篩選,ES 的查詢結(jié)果僅包含記錄的唯一標(biāo)識(shí) sku_id(其實(shí)還包含 ES 為每條文檔記錄的 doc_id)
          • Hbase 是列存儲(chǔ)數(shù)據(jù)庫(kù),每列數(shù)據(jù)有一個(gè) rowKey。利用 rowKey 篩選一條記錄時(shí),復(fù)雜度為 O(1)。(類似于從 HashMap 中根據(jù) key 取 value)
          • 根據(jù) ES 查詢返回的唯一標(biāo)識(shí) sku_id,作為 Hbase 查詢中的 rowKey,在 O(1) 復(fù)雜度下獲取其他信息字段,如價(jià)格,庫(kù)存等

          830cae4e15e77451c2818a5235a36be0.webp

          圖片

          使用 ES + Hbase 組合查詢方案,在線上進(jìn)行了小規(guī)模的灰度測(cè)試。在 1000W 量級(jí)的底池?cái)?shù)據(jù)中篩選 10W 的數(shù)據(jù),對(duì)比 CK 翻頁(yè)方案,最壞耗時(shí)從 10~18s 優(yōu)化到了 3~6s 左右。

          也應(yīng)該看到,使用 ES + Hbase 組合查詢方案,會(huì)增加系統(tǒng)復(fù)雜度,同時(shí)數(shù)據(jù)也需要同時(shí)存儲(chǔ)到 ES 和 Hbase。

          RediSearch+RedisJSON 優(yōu)化方案

          RediSearch 是基于 Redis 構(gòu)建的分布式全文搜索和聚合引擎,能以極快的速度在 Redis 數(shù)據(jù)集上執(zhí)行復(fù)雜的搜索查詢。

          RedisJSON 是一個(gè) Redis 模塊,在 Redis 中提供 JSON 支持。RedisJSON 可以和 RediSearch 無(wú)縫配合,實(shí)現(xiàn)索引和查詢 JSON 文檔。

          根據(jù)一些參考資料,RediSearch + RedisJSON 可以實(shí)現(xiàn)極高的性能,可謂碾壓其他 NoSQL 方案。在后續(xù)版本迭代中,可考慮使用該方案來(lái)進(jìn)一步優(yōu)化。

          下面給出 RediSearch + RedisJSON 的部分性能數(shù)據(jù)。

          RediSearch 性能數(shù)據(jù)

          在同等服務(wù)器配置下索引了 560 萬(wàn)個(gè)文檔 (5.3GB),RediSearch 構(gòu)建索引的時(shí)間為 221 秒,而 Elasticsearch 為 349 秒。RediSearch 比 ES 快了 58%。

          115cd5c7536fa01d9f8a671bab816af0.webp

          圖片

          數(shù)據(jù)建立索引后,使用 32 個(gè)客戶端對(duì)兩個(gè)單詞進(jìn)行檢索,RediSearch 的吞吐量達(dá)到 12.5K ops/sec,ES 的吞吐量為 3.1K ops/sec,RediSearch 比 ES 要快 4 倍。

          同時(shí),RediSearch 的延遲為 8ms,而 ES 為 10ms,RediSearch 延遲稍微低些。

          RedisJSON 性能數(shù)據(jù)

          根據(jù)官網(wǎng)的性能測(cè)試報(bào)告,RedisJson + RedisSearch 可謂碾壓其他 NoSQL:

          • 對(duì)于隔離寫入(isolated writes),RedisJSON 比 MongoDB 快 5.4 倍,比 ES 快 200 倍以上
          • 對(duì)于隔離讀取(isolated reads),RedisJSON 比 MongoDB 快 12.7 倍,比 ES 快 500 倍以上

          在混合工作負(fù)載場(chǎng)景中,實(shí)時(shí)更新不會(huì)影響 RedisJSON 的搜索和讀取性能,而 ES 會(huì)受到影響:

          • RedisJSON 支持的操作數(shù)/秒比 MongoDB 高約 50 倍,比 ES 高 7 倍/秒
          • RedisJSON 的延遲比 MongoDB 低約 90 倍,比 ES 低 23.7 倍

          此外,RedisJSON 的讀取、寫入和負(fù)載搜索延遲,在更高的百分位數(shù)中遠(yuǎn)比 ES 和 MongoDB 穩(wěn)定。

          當(dāng)增加寫入比率時(shí),RedisJSON 還能處理越來(lái)越高的整體吞吐量。而當(dāng)寫入比率增加時(shí),ES 會(huì)降低它可以處理的整體吞吐量。

          總結(jié)

          本文從一個(gè)業(yè)務(wù)訴求觸發(fā),對(duì)“千萬(wàn)量級(jí)數(shù)據(jù)中查詢 10W 量級(jí)的數(shù)據(jù)”介紹了不同的設(shè)計(jì)方案。

          對(duì)于在 1000W 量級(jí)的底池?cái)?shù)據(jù)中篩選 10W 的數(shù)據(jù)的場(chǎng)景,不同方案的耗時(shí)如下:

          • 多線程+CK 翻頁(yè)方案,最壞耗時(shí)為 10s~18s
          • 單線程+ES scroll scan 深翻頁(yè)方案,相比 CK 方案,并未見(jiàn)到明顯優(yōu)化
          • ES+Hbase 組合方案,最壞耗時(shí)優(yōu)化到了 3s~6s
          • RediSearch+RedisJSON 組合方案,后續(xù)會(huì)實(shí)測(cè)該方案的耗時(shí)
              
                
                    
                      
                        
                          
                            
                              

          1.?Spring Cloud Sleuth 全鏈路日志跟蹤解決方案(強(qiáng)烈推薦)

          2.?個(gè)人網(wǎng)站總被攻擊?寫個(gè)自動(dòng)封禁IP的腳本給你!

          3.?面試官:你這JVM調(diào)優(yōu),回答的很有問(wèn)題呀!!

          4.?SpringBoot版的低代碼開(kāi)發(fā)平臺(tái),關(guān)聯(lián)無(wú) SQL,性能高10倍!

                                

          最近面試BAT,整理一份面試資料 Java面試BATJ通關(guān)手冊(cè) ,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“ 在看 ”,關(guān)注公眾號(hào)并回復(fù)? Java ?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

                                

          PS:因公眾號(hào)平臺(tái)更改了推送規(guī)則,如果不想錯(cuò)過(guò)內(nèi)容,記得讀完點(diǎn)一下 在看 ,加個(gè) 星標(biāo) ,這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。

          點(diǎn)“在看”支持小哈呀,謝謝啦

          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  18禁日韩无码 | 欧美自拍视频在线观看 | 爱草逼爱草逼爱草逼爱草逼爱草逼爱草逼 | 日韩无码高清电影 | 天堂草原电视剧一念天堂草原电视剧v8给我发 |