Kylin#KYLIN(Top-N 近似預(yù)計(jì)算)

kylin3之前的TopN實(shí)現(xiàn)原理

看一個(gè)典型的 Top-N 查詢示例。該查詢是選擇在 2015 年 10 月 1 日抗楔,地址在北京惯雳,銷售商品按價(jià)格之和排序(倒序),找前 100 個(gè)妥箕。

示例

在 Kylin v1.5 之前滥酥,SQL 中的 group by 列,需聲明成維度畦幢,所以這個(gè) Cube 的維度中要有日期坎吻,地點(diǎn)和商品名,度量是 SUM(PRICE) 宇葱。圖中展示了一個(gè)這樣設(shè)計(jì) Cube瘦真。因?yàn)樯唐返幕鶖?shù)很大刊头,計(jì)算的 cuboid 的行數(shù)會(huì)很多;而度量值 SUM(PRICE) 是非排序的诸尽,因此需要將這些紀(jì)錄都從存儲(chǔ)器讀到 Kylin 查詢引擎中(內(nèi)存)原杂, 然后再排序找出最高的紀(jì)錄;這樣的解決辦法總開銷較大弦讽。


用普通度量處理 Top N 查詢

Kylin 選擇了 Space-Saving 算法 污尉,以及它的一個(gè)衍生版 Parallel Space-Saving,并在此之上做了特定的優(yōu)化往产,有了 Top-N 之后被碗,Cube 的設(shè)計(jì)會(huì)比以前簡(jiǎn)單很多,因?yàn)橄駝偛诺纳唐访麜?huì)被挪到 Measure 中去仿村,在 Measure 里按 Sum 值做倒序锐朴,只保留最大的若干值


使用 Top N 度量的 Cube

值得一提的是需要用多少空間運(yùn)算 Top-N。簡(jiǎn)單來(lái)說(shuō)存儲(chǔ)空間越多準(zhǔn)確率越高蔼囊。我們通過(guò)使用生成一些樣本數(shù)據(jù)然后用 Space-Saving 計(jì)算焚志,并且跟真實(shí)結(jié)果做比較,發(fā)現(xiàn) 50 倍空間對(duì)于普通的數(shù)據(jù)分布是夠用的畏鼓。也即酱酬,用戶需要 Top 100 的結(jié)果,Kylin 對(duì)于每種組合條件值云矫,保留 Top 5000 的紀(jì)錄, 并供以后再次合并膳沽。這樣即使多次合并, Top100 依然是比較接近真實(shí)結(jié)果

Top-N 的優(yōu)點(diǎn):因?yàn)樗槐A?Top 的記錄让禀,會(huì)讓 Cube 空間大幅度減少挑社,而查詢性能大大提升。在一個(gè)典型的例子里巡揍,改用 Top-N 后痛阻,Cube 的大小減少了 90%,而查詢時(shí)間則只有以前的 10% 不到腮敌。
缺點(diǎn)是它可能是近似的結(jié)果(當(dāng) 50 倍空間也無(wú)法容納所有基數(shù)的時(shí)候)阱当。如果業(yè)務(wù)場(chǎng)景需要絕對(duì)精確的話,它可能不適合糜工。

Top-N 誤差率由很多因素決定的:
數(shù)據(jù)的分布:數(shù)據(jù)分布越陡斗这,誤差越小。
算法使用的空間:如果對(duì)精度要求高的話啤斗,可以選擇用更多的空間換取更精準(zhǔn)的準(zhǔn)確率 表箭。在實(shí)際使用中,可以做一些比較以了解誤差情況。

kylin3之后的TopN實(shí)現(xiàn)原理

當(dāng)前Kylin4的TopN UDAF注冊(cè)是在org.apache.kylin.engine.spark.job.CuboidAggregator#aggInternal, 代碼如下:

def aggInternal(ss: SparkSession,
                  dataSet: DataFrame,
                  dimensions: util.Set[Integer],
                  measures: util.Map[Integer, FunctionDesc],
                  isSparkSql: Boolean): DataFrame = {
      //省略
      measure.expression.toUpperCase(Locale.ROOT) match {
        //省略
        case "TOP_N" =>
          // Uses new TopN aggregate function
          // located in kylin-spark-project/kylin-spark-common/src/main/scala/org/apache/spark/sql/udaf/TopN.scala
          val schema = StructType(measure.pra.map { col =>
            val dateType = col.dataType
            if (col == measure) {
              StructField(s"MEASURE_${col.columnName}", dateType)
            } else {
              StructField(s"DIMENSION_${col.columnName}", dateType)
            }
          })

          if (reuseLayout) {
            new Column(ReuseTopN(measure.returnType.precision, schema, columns.head.expr)
              .toAggregateExpression()).as(id.toString)
          } else {
            new Column(EncodeTopN(measure.returnType.precision, schema, columns.head.expr, columns.drop(1).map(_.expr))
              .toAggregateExpression()).as(id.toString)
          }
       //省略
        case _ =>
          max(columns.head).as(id.toString)
      }
    }.toSeq
//省略
    if (reuseLayout) {
      val columns = NSparkCubingUtil.getColumns(dimensions) ++ measureColumns(dataSet.schema, measures)
      df.select(columns: _*)
    } else {
      df
    }
  }

其實(shí)TopN最初的實(shí)現(xiàn)的在org.apache.kylin.engine.spark.job.TopNUDAF免钻,但是可以看到目前TopN的實(shí)現(xiàn)是在org.apache.spark.sql.udaf.BaseTopN.scala彼水,最新的實(shí)現(xiàn)主要針對(duì)舊的實(shí)現(xiàn)修復(fù)了性能問(wèn)題,詳情可以查看KYLIN-4760极舔。

Kylin 4.0的TopN是通過(guò)Spark UDAF的方式實(shí)現(xiàn)的凤覆,以下是實(shí)現(xiàn)類接口之間的關(guān)系,可以看到最終實(shí)現(xiàn)的是BaseTopN拆魏,繼承的是TypedImperativeAggregate盯桦。然后BaseTopN又有兩個(gè)子類,分別是EncodeTopN和ReuseTopN渤刃,當(dāng)從平表(FlatTable)開始構(gòu)建的時(shí)候拥峦,F(xiàn)latTable中沒(méi)有構(gòu)建過(guò)TopN,這里會(huì)調(diào)用EncodeTopN卖子,再次之后從已經(jīng)構(gòu)建好的cuboid構(gòu)建下一層cuboid的時(shí)候會(huì)調(diào)用ReuseTopN略号,避免重復(fù)計(jì)算,接口關(guān)系圖如下:


image.png

繼承TypedImperativeAggregate實(shí)現(xiàn)TopN洋闽,而不是UserDefineAggregateFunction主要是因?yàn)閁serDefinedAggregateFunction 是把 catalyst 內(nèi)部 internalRow 類型轉(zhuǎn)換為了 Row 類型玄柠,然后使用用戶自己的 update 方法處理,然后TypedImperativeAggregate需要自己做序列化诫舅、反序列化處理羽利,少了一層轉(zhuǎn)換。

TopNCounter介紹

前面提到Space-Saving算法是在TopNCounter中實(shí)現(xiàn)的刊懈,此處我們對(duì)TopNCounter的實(shí)現(xiàn)進(jìn)行一個(gè)簡(jiǎn)要的介紹这弧。BaseTopN對(duì)象初始化的時(shí)候會(huì)創(chuàng)建TopNCounter對(duì)象,用戶保存計(jì)算過(guò)程中符合TopN條件的行俏讹,對(duì)應(yīng)于Spark UDAF的概念是aggregate buffer。update畜吊,merge泽疆,eval都是處理的TopNCounter。TopNCounter在初始化的時(shí)候需要指定容量, 大小建議為N * TopNCounter.EXTRA_SPACE_RATE, 其中N為TopN定義的大小玲献,EXTRA_SPACE_RATE為建議額外空間調(diào)整參數(shù)殉疼,默認(rèn)為10, 也就是說(shuō)如果定義的topn(10,4), 那么TopNCounter的初始化大小則為10 * 10 = 100 捌年。

TopN的處理流程可以見下圖:


image.png

update()主要將傳入的行通過(guò)TopNCounter.offer() 將一行的內(nèi)容插入到TopNCounter對(duì)象中瓢娜,merge則是對(duì)兩個(gè)經(jīng)過(guò)update()操作的group進(jìn)行去重合并,最后在eval()的時(shí)候調(diào)用TopNCounter.sortAndRetain()來(lái)排序和調(diào)整TopNCounter大小礼预,最終得到聚合結(jié)果眠砾。

Kylin 4.0目前使用的是parquet進(jìn)行存儲(chǔ),我們定義topn(10,4)托酸, TopNCounter.EXTRA_SPACE_RATE 設(shè)置為1褒颈。cuboid中維度和度量列明的映射關(guān)系為:
0 -> seller_id
1 -> item_id
2 -> id
3 -> price
4 -> Count
5 -> TopN

如下是只有TopN和只有SUM的cuboid內(nèi)容:

image.png

值得注意的是第二行柒巫,Count為11,但是實(shí)際上TopN列只存儲(chǔ)了10個(gè)值谷丸,這是因?yàn)門opNCounter的容量只有10 * EXTRA_SPACE_RATE = 10堡掏, 超過(guò)10的內(nèi)容不會(huì)被存儲(chǔ),這也是當(dāng)前TopN存在誤差的原因所在刨疼∪洌可以看到TopN將計(jì)算的維度和group by的維度放到了一起,然后用數(shù)組的形式進(jìn)行存儲(chǔ)揩慕。


image.png

對(duì)于sum度量亭畜,kylin則是直接存儲(chǔ)的sum后的聚合值。

參考漩绵,其實(shí)是抄了一邊
Apache Kylin的Top-N近似預(yù)計(jì)算-InfoQ
Kylin 4.0 TopN Introduction CN - Kylin 4.0 TopN Introduction CN - Apache Software Foundation
Apache Kylin權(quán)威指南(九):Top_N 度量?jī)?yōu)化-InfoQ

問(wèn)題:

  1. 對(duì)于Kylin3贱案,是哪個(gè)維度會(huì)移入到 Measure中(不定義為維度實(shí)現(xiàn),我感覺(jué)怎么是個(gè)鎖)止吐,多個(gè)維度移入到 Measure又是如何實(shí)現(xiàn)
  2. TopNCounter中的方法調(diào)用流程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宝踪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碍扔,更是在濱河造成了極大的恐慌瘩燥,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件不同,死亡現(xiàn)場(chǎng)離奇詭異厉膀,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)二拐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門服鹅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人百新,你說(shuō)我怎么就攤上這事企软。” “怎么了饭望?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵仗哨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我铅辞,道長(zhǎng)厌漂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任斟珊,我火速辦了婚禮苇倡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己雏节,他們只是感情好胜嗓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钩乍,像睡著了一般辞州。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寥粹,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天变过,我揣著相機(jī)與錄音,去河邊找鬼涝涤。 笑死媚狰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阔拳。 我是一名探鬼主播崭孤,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼糊肠!你這毒婦竟也來(lái)了辨宠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤货裹,失蹤者是張志新(化名)和其女友劉穎嗤形,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弧圆,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赋兵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搔预。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霹期。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拯田,靈堂內(nèi)的尸體忽然破棺而出历造,到底是詐尸還是另有隱情,我是刑警寧澤勿锅,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布帕膜,位于F島的核電站枣氧,受9級(jí)特大地震影響溢十,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜达吞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一张弛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦吞鸭、人聲如沸寺董。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遮咖。三九已至,卻和暖如春造虏,著一層夾襖步出監(jiān)牢的瞬間御吞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工漓藕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陶珠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓享钞,卻偏偏與公主長(zhǎng)得像揍诽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栗竖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355