看看Quora是如何實現貢獻者排行功能的
今年年初,我們提出了最大訪問量作者(MVWs)功能,即識別那些他們了解和關心的主題的最活躍貢獻者。MVWs 是一種非常好的方式,既可以彰顯貢獻者,又可以幫助讀者發現貢獻者自身關注的主題。
作為一個產品工程師(product engineer),全程投入到這個功能的開發和設計中,我很高興有這樣一次機會,可以結合我的技術和熱情來創建一個用戶體驗良好的產品。在這個過程中,我能夠加入到產品經理、產品設計和數據科學家中來確定產品的最終目標和完成該目標的最佳方式。
在這篇文章中,我們將深入討論該功能的某些模塊,以及各種場景背后的架構力量,以及如何將這些模塊整合完成最后的MVWs 功能。
最大訪問量作者(MVWs)的定義
一個主題的最大訪問量作者(MVMs)是指最近 30 天內,該主題下訪問量最多的公共回答的用戶。因此,我們的最終目標是識別每個主題下的活躍作者。完成該目標的第一步是定義一個清晰的范疇。
更明確的說,我們想創建一個能完成下列功能的系統:
1、跟蹤近 30 天內每個答案的訪問次數。
2、給定一個主題,返回最近 30 天內按答案訪問量排名的 MVWs 列表。
3、給定一個用戶,返回該用戶屬于 MVW 的主題列表。
4、給定一個主題,返回新的 MVWs 用戶列表,這些用戶將收到通知。
正如我們所見,清晰的定義 MVW 產品需求,直接關系到正確選擇最好的架構來滿足需求。
訪問數據流
在跳到如何使用訪問數據來創建 MVW 之前,我們先后退一步,弄清楚訪問數據是從哪來的?
我們在產品的不同部分使用實時訪問量來顯示每段內容的查看情況。無論什么時候有人在Quora上查看了內容的某個片段,例如一個答案,我都能對訪問量加1,并且我們想在持久化存儲中保存所有的數據,因為不想弄丟任何數據。一種比較天真的實現方式是通過如下的方式來記錄訪問量:
#Views module (version 1)
# def log_views(answer_id, count=1):
# Called every time when there is a view # Single update that happens inline views_datastore.increment(answer_id, count=count)</pre>
這看起來相當簡單,對吧?如果系統只需要處理每秒 10 次的訪問量,也就是說每 100 毫秒增加1次訪問,那這是相當合理的。但是,如果訪問次數是每秒100次?1000次?甚至更多?如果系統中有成千上萬的答案,并且這些答案都會源源不斷的獲取到新的流量,系統將會是什么樣子?
對于一個產品來說,訪問是最頻繁的操作(基本上每個頁面的加載都會有),如果持久化每個行為將會對落地真實數據(ground-truth data)存儲(HBase)產生太多的寫操作,從而無法滿足可擴展性問題。一種更好的可擴展方案是使用事件隊列,然后按一定的規則進行批量增加。
#Views module (version 2)
#
def enqueue(answer_id, count=1):
# Called every time when there is a view # Enqueue to be updated via "batch_update" views_event_queue.enqueue(Item(answer_id, count))
def batch_update():
# Process enqueued items regularly items = views_event_queue.dequeue_recent_items() # Batch update views_datastore.batch_increment(items)</pre>
對于這種設計,訪問數據將先進入一個簡單的持久化事件隊列(在Redis上實現的)。有了事件隊列,我們可以有效的分攤 I/O 延遲,這樣可以大幅度提高系統的吞吐量。從產品的角度來看,如果我們展示訪問次數的頻率比批量操作頻率更高,則實時的訪問量可以通過首次獲取落地真實數據,此后通過對Redis緩存中的數據進行遞增來實現。下面是該流程的簡化圖:
![]()
聚合訪問量到 MVWs
現在,我們已經在某個地方有了實時訪問數據,現在我們試著在這上面創建 MVW。
通過產品定義,一個最重要的步驟是通過每個答案的訪問量來為每個用戶聚合每個主題的訪問量。這種聚合需要花費大量的計算來整合多個數據源,包括答案的訪問次數,問題-主題之間的關系,問題-答案之間的關系。
此外,任何時候如果有答案被刪除,都會引起級聯效果。已刪除答案的訪問次數不應該包含在與此相關主題的用戶排名范圍以內,這就意味著我們需要對這些主題的排名進行更新。同樣,如果某個答案出現了質量問題,也會出現類似的情況。這樣就會導致更多的計算和聚合。
并且我們也開始注意到,Quora系統許多已存在的功能也會影響 MVW 的設計。下面是計算 MVW 數據所需要的實體之間的整體依賴關系圖:
![]()
用戶期望在訪問 MVW 頁面時能夠快速的加載。但是,聚合操作很慢,并且頁面加載時計算機之間的通信也無法滿足,這些數據需要在頁面加載前緩存起來。但是,如果這些依賴的項一直在改變,我們就需要不停的重新計算才能緩存。
關于訪問頻率的記錄我們已在上面探討過,如果任何訪問量的改變都要反應到排名,我們就需要進行大量的重新聚合操作。我們當然可以投入更多的技術資源來支持這種復雜的場景,但是在這之前,我們考慮這是否真的是產品的需求?
在那天快結束的時候,我們的目標不僅僅是解決一個困難的工程問題,而是通過一個好的產品特性來實現最佳用戶體驗。回到原始的目標,即識別每個主題的優秀作者,我們確定的是在一個主題有新的 MVW 時,需要通知用戶。如果排名非常不穩定,每秒都有可能變化,一個新的 MVW 可能剛接到通知到達前10,打開時才發現,數據已經過時了。
經過工程師、設計師、數據科學家及產品經理的討論,我們覺得這樣頻繁的重新計算如此復雜的聚合是一個非常不好的主意,因為:
1、實時排名引起技術點復雜性。上圖中任何依賴的改變(如,有訪問行為時或者答案被刪除時)都會觸發排名的改變。
2、從可用性角度考慮,實時排名也是非常不好的。不穩定的排名會引起用戶的混亂。
我們已經確定,對所有狀態改變敏感的排名不是產品的需求(實際上也不是所期望的)。相反,一種更好的選擇是周期性預聚合 MVWs 的離散版本,而不是像上面的流程一樣,依賴實時的訪問情況。我們可以通過線下的批量更新來計算下一個版本的 MVWs。
再看訪問數據流
為了支持線下的計算,我們通過第二條線下查詢的流水線來上傳訪問數據。有了這個設置,單個訪問被記錄在一個web 服務器的訂閱(Scribe)上,這樣通過多階段線下數據流,我們可以將數據上傳到 Amazon S3,并最終導入到 Redshift。我們之所以選擇 Redshift,是因為在這上面做聚合操作非常簡單,很適合大規模復雜查詢。除了訪問數據,其他相關的數據也可以上傳到 Redshift。
該流水線可以抽象為下圖所示:
![]()
最大訪問作者,從開始到完成:
現在,有了批量更新 MVWs 的所有數據,讓我們走一遍真正計算排名的流程。
在討論最終的架構之前,先重新看一下我們想檢索的信息:
1、給定一個主題,返回最近 30 天內按答案訪問量排名的 MVWs 列表。
2、給定一個用戶,返回該用戶屬于 MVW 的主題列表。
3、給定一個主題,返回新的 MVWs 用戶列表,并給他們發送通知。
我們選擇使用HBase(一個開源的,非關系型的,基于 Google's BigTable的分布式數據庫)作為我們主要的后端來持久化存儲 MVW 的緩存數據。一個好處是使用HBase能獲得比關系型數據庫管理系統(RDBMS),如 MySQL,或者是簡單的內存緩存,如Redis,更好的大數據量的讀寫性能。由于所有的 MVW 數據都是預聚合的,所以只需要簡單的key-value查詢,而不要求范圍或聚合查詢。因此,我們的使用場景不涉及到需要關系型數據庫索引的復雜查詢。
例如,我們想緩存一些預聚合數據來支持如下1—10排名的查詢:
(Topic, Rank) -> (User, TopicViewCount)
我們可以創建一個關鍵字為“topic”和“rank”的HBase表格,每一行需要一個“user”列和一個“topic view count”列。HBase的另一個非常好的特性是,我們實際上可以定義一個“user”的列族和一個“topic view count”列族,每個包含一系列的版本值。通過每個列族,HBase允許動態的聚合新的列。每次周期性更新計算出來的新版本排名時,我們都可以創建一個新的列來存儲,通過直接指定這些列的存活時間,老版本就會自動過期。
回顧一下,我們記錄訪問數據到線下的數據流(Redshift)中,預聚合每個主題的排名,并在HBase中緩存結果,在最終的 MVW 功能中能夠高效的查詢數據。下面這個圖總結了從訪問記錄到 MVW 數據的聚合到如何使用這些數據的流程:
![]()
在Quora的產品工程中的教訓
通過創建 MVWs 的過程,我學到了許多關于產品工程的有效方式。該項目的成功主要歸結為對如下原則的堅持:
1、明確定義技術和功能目標。例如,通過清晰的映射產品功能目標與完整的依賴圖(如上),我們能夠避免出現因很小的改變而引起的極度不穩定排名,并且這種排名對用戶體驗沒有任何價值。這有助于我們做出正確的權衡,實現一個基于版本的設計,這既有利于技術層面的擴展,也有利于用戶端的直觀感受。
2、支持簡潔性和可擴展性。如果有疑問,寧可降低復雜性。從產品角度來說,這可以給用戶提供一個更清晰易懂的排名功能,從技術的角度來說,為我們在一個簡單易懂的系統上持續迭代打開了一扇大門。
總之,作為一個產品工程師,我已經能夠更好的看到 MVW 的影響,我也期待在未來能做更多的改進。對創建Quora的下一個精彩的功能有興趣嗎?查看職業生涯頁獲取更多關于在這里作為產品工程師的感受!
原文鏈接: The Product Engineering Behind Most Viewed Writers
譯者: 杰微刊 --劉曉鵬
來自: http://www.cocoachina.com/programmer/20160201/15178.html