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'sscore
是會隨時間變化增減。
上面所說堆的優化無法處理這樣的問題,底下舉個例子。假設今天有一個 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邮件归档
其实 Flink 对 Top-N 问题并没有很 fancy 的实现... Flink 把 Top-N 问题分成三种情况:
数据只添加,不更新不删除(就像 batch mode) 这种情况的实现是 AppendOnlyTopNFunction,就像你说的一样使用一个 Map 来维护。不能直接使用堆来维护的原因是:因为要告知下游每一条记录的精确排名。
数据可能有添加和更新 这种情况的实现是 UpdatableTopNFunction,但是这个类开头的注释表明了它只能用于以下特殊情况:
不能删数据或者撤回数据。 这种情况就避免了你上面说的排名变大,导致掉出 Top-N 的情况。还是可以用一个 Map 来维护。
数据可以添加、更新和删除 这种情况的实现是 RetractableTopNFunction。因为数据更新 / 删除后可能会掉出 Top-N,要找新数据补进来,那么只能从 state 里捞应该补进来的数据。当前由于社区没有 SortedMapState 的实现,现在是用 ValueState<SortedMap<>> 存 state。每次读 state 都是把整个 state 拿出来读的,所以数据量大了其实没办法用... 等社区引入了 SortedMapState 以后,就可以用 iterator 只读取前面一些我们想要补进来的数据。*来自志愿者整理的flink
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。