开发者社区> 问答> 正文

關於如何在流數據上計算 Top K 的應用問題

Hi,

最近正在研究 Top K 的問題,在研究中找到了 Blink SQL 可以透過維護一個儲存 K 的最大紀錄的 ”堆”來優化底下這類 SQL,不過我認為這只能針對 score 只會增加不減少的情況。

SELECT user_id, score FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num FROM user_scores) WHERE row_num <= 3 我的問題是當如果這樣的計算是應用在流數據上,且 score 可能隨時間增加或是“減少”的話,例 如底下這類的 SQL,能有什麼樣的優化?

SELECT user_id, score FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num FROM ( SELECT user_id, LAST_VAL(score) AS score FROM user_scores GROUP BY user_id)) WHERE row_num <= 3

SQL 中的 user_scores 可以當作是從 DataStream 直接轉換過來的 Dynamic Table, LAST_VAL假設是一種 UDAF,可以挑出目前最新的值。所以,可以想像這張 table 的 user's score 是會隨時間變化增減。

上面所說堆的優化無法處理這樣的問題,底下舉個例子。假設今天有一個 top-3 的堆中已經存放 了三個使用者:A, B, C,各自的 scores 是:4, 3, 2,接下來收到了一個使用者 D 和他的分數是 1 的話,這個時候演算法會直接忽略掉 D,因為他不在 top-3 的範圍內。但是當下一個如果收到 的是一個更新 A 使用者的 score 為 0 的紀錄的話,這個時候理論上我們知道 top-3 會改為 B, C, D,但是在維護 top-3 的堆中我們無力找回被忽略的使用者 D。這樣的優化在 batch mode 是沒有 問題的,因為最新的 score 在有限的數據中會是固定的不動的。

不過當處理流數據,我目前只想到這種應用最終可能需要退回成存放全部使用者 scores 才有辦 法處理,才能隨時計算出正確的 top-k。所以我想請教各位大牛有沒有什麼樣的優化方式可以處 理這樣的問題,讓狀態不需要存到全部資料?當然這個問題不侷限在 SQL,如果有任何實作在 DataStream 上的優化都是可接受。感謝大家幫忙。*来自志愿者整理的flink邮件归档

展开
收起
雪哥哥 2021-12-07 16:11:04 407 0
1 条回答
写回答
取消 提交回答
  • 其实 Flink 对 Top-N 问题并没有很 fancy 的实现... Flink 把 Top-N 问题分成三种情况:

    1. 数据只添加,不更新不删除(就像 batch mode) 这种情况的实现是 AppendOnlyTopNFunction,就像你说的一样使用一个 Map 来维护。不能直接使用堆来维护的原因是:因为要告知下游每一条记录的精确排名。

    2. 数据可能有添加和更新 这种情况的实现是 UpdatableTopNFunction,但是这个类开头的注释表明了它只能用于以下特殊情况:

    3. 数据更新后排名只能变小不能变大;
    4. 数据的 sort key 要 unique;
    5. 不能删数据或者撤回数据。 这种情况就避免了你上面说的排名变大,导致掉出 Top-N 的情况。还是可以用一个 Map 来维护。

    6. 数据可以添加、更新和删除 这种情况的实现是 RetractableTopNFunction。因为数据更新 / 删除后可能会掉出 Top-N,要找新数据补进来,那么只能从 state 里捞应该补进来的数据。当前由于社区没有 SortedMapState 的实现,现在是用 ValueState<SortedMap<>> 存 state。每次读 state 都是把整个 state 拿出来读的,所以数据量大了其实没办法用... 等社区引入了 SortedMapState 以后,就可以用 iterator 只读取前面一些我们想要补进来的数据。*来自志愿者整理的flink

    2021-12-07 16:34:12
    赞同 展开评论 打赏
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载