<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>

          CK、ES、RediSearch 對比,誰的性能更勝一籌

          共 8845字,需瀏覽 18分鐘

           ·

          2022-06-13 13:47

          點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)”


          回復(fù)”學(xué)習(xí)資料“獲取學(xué)習(xí)寶典

          文章來源:https://c1n.cn/EmgJv

          目錄
          • 前言

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

          • CK 分頁查詢

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

          • ES+Hbase 組合查詢方案

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

          • 總結(jié)


          前言


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


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

          • 多線程+CK 翻頁方案

          • ES scroll scan 深翻頁方案

          • ES+Hbase 組合方案

          • RediSearch+RedisJSON 組合方案


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


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

          • 先根據(jù)配置的「篩選規(guī)則」,從底池表中篩選出「目標(biāo)數(shù)據(jù)」

          • 在根據(jù)配置的「排序規(guī)則」,對「目標(biāo)數(shù)據(jù)」進(jìn)行排序,得到「結(jié)果數(shù)據(jù)」


          技術(shù)方案如下:


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


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


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

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

          List<Map<StringObject>> result = Lists.newArrayList();
          List<Future<List<Map<StringObject>>>> futureList = new ArrayList<>(pageCnt);

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


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


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


          CK 分頁查詢


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


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

          public List<Map<StringObject>> queryPoolSkuList( Map<StringObject> params ) {
              List<Map<StringObject>> resultMaps = new ArrayList<>();

              QueryCondition queryCondition = parseQueryCondition(params);
              List<Map<StringObject>> 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<StringObject>> 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 分頁查詢時,是通過 limit #{limitStart},#{limitEnd} 實(shí)現(xiàn)的分頁。


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


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


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


          | ES 的翻頁方案

          ES 翻頁,有下面幾種方案:

          • from + size 翻頁

          • scroll 翻頁

          • scroll scan 翻頁

          • search after 翻頁

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


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

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

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

          • 底池量級越大,查詢相同的數(shù)據(jù)量,耗時越大

          • 查詢結(jié)果 3W 以下時,ES 性能優(yōu);查詢結(jié)果 5W 以上時,CK 多線程性能優(yōu)


          ES+Hbase 組合查詢方案


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


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


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


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

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

          • 一次 ES 查詢中,若查詢字段和信息較多,fetch 階段的耗時,遠(yuǎn)大于 query 階段的耗時。

          • 一次 ES 查詢中,若查詢字段和信息較多,通過減少不必要的查詢字段,可以顯著縮短查詢耗時。


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


          | ES 查詢的兩個階段

          在 ES 中,搜索一般包括兩個階段:

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

          • fetch 階段:根據(jù) query 階段返回的文檔 ID(doc_id),取出具體的文檔(doc)


          | 組合使用 Hbase

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


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

          • ES 僅用于條件篩選,ES 的查詢結(jié)果僅包含記錄的唯一標(biāo)識 sku_id(其實(shí)還包含 ES 為每條文檔記錄的 doc_id)

          • Hbase 是列存儲數(shù)據(jù)庫,每列數(shù)據(jù)有一個 rowKey。利用 rowKey 篩選一條記錄時,復(fù)雜度為 O(1)。(類似于從 HashMap 中根據(jù) key 取 value)

          • 根據(jù) ES 查詢返回的唯一標(biāo)識 sku_id,作為 Hbase 查詢中的 rowKey,在 O(1) 復(fù)雜度下獲取其他信息字段,如價(jià)格,庫存等

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


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


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


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


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


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


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


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

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

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


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


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

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

          • 對于隔離寫入(isolated writes),RedisJSON 比 MongoDB 快 5.4 倍,比 ES 快 200 倍以上

          • 對于隔離讀取(isolated reads),RedisJSON 比 MongoDB 快 12.7 倍,比 ES 快 500 倍以上


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

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

          • RedisJSON 的延遲比 MongoDB 低約 90 倍,比 ES 低 23.7 倍


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


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


          總結(jié)


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


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

          • 多線程+CK 翻頁方案,最壞耗時為 10s~18s

          • 單線程+ES scroll scan 深翻頁方案,相比 CK 方案,并未見到明顯優(yōu)化

          • ES+Hbase 組合方案,最壞耗時優(yōu)化到了 3s~6s

          • RediSearch+RedisJSON 組合方案,后續(xù)會實(shí)測該方案的耗時


          -------------  END  -------------
          掃描下方二維碼,加入技術(shù)群。暗號:加群



          瀏覽 88
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  久久做| 欧美啊啊啊啊啊啊啊 | 精品无码免费一区二区 | 精品 码产区一区二\ | 手机看片人妻 |