看看Quora是如何實(shí)現(xiàn)貢獻(xiàn)者排行功能的
今年年初,我們提出了***訪問量作者(MVWs)功能,即識(shí)別那些他們了解和關(guān)心的主題的最活躍貢獻(xiàn)者。MVWs 是一種非常好的方式,既可以彰顯貢獻(xiàn)者,又可以幫助讀者發(fā)現(xiàn)貢獻(xiàn)者自身關(guān)注的主題。
作為一個(gè)產(chǎn)品工程師(product engineer),全程投入到這個(gè)功能的開發(fā)和設(shè)計(jì)中,我很高興有這樣一次機(jī)會(huì),可以結(jié)合我的技術(shù)和熱情來創(chuàng)建一個(gè)用戶體驗(yàn)良好的產(chǎn)品。在這個(gè)過程中,我能夠加入到產(chǎn)品經(jīng)理、產(chǎn)品設(shè)計(jì)和數(shù)據(jù)科學(xué)家中來確定產(chǎn)品的最終目標(biāo)和完成該目標(biāo)的***方式。
在這篇文章中,我們將深入討論該功能的某些模塊,以及各種場(chǎng)景背后的架構(gòu)力量,以及如何將這些模塊整合完成***的MVWs 功能。
***訪問量作者(MVWs)的定義
一個(gè)主題的***訪問量作者(MVMs)是指最近 30 天內(nèi),該主題下訪問量最多的公共回答的用戶。因此,我們的最終目標(biāo)是識(shí)別每個(gè)主題下的活躍作者。完成該目標(biāo)的***步是定義一個(gè)清晰的范疇。
更明確的說,我們想創(chuàng)建一個(gè)能完成下列功能的系統(tǒng):
1、跟蹤近 30 天內(nèi)每個(gè)答案的訪問次數(shù)。
2、給定一個(gè)主題,返回最近 30 天內(nèi)按答案訪問量排名的 MVWs 列表。
3、給定一個(gè)用戶,返回該用戶屬于 MVW 的主題列表。
4、給定一個(gè)主題,返回新的 MVWs 用戶列表,這些用戶將收到通知。
正如我們所見,清晰的定義 MVW 產(chǎn)品需求,直接關(guān)系到正確選擇***的架構(gòu)來滿足需求。
訪問數(shù)據(jù)流
在跳到如何使用訪問數(shù)據(jù)來創(chuàng)建 MVW 之前,我們先后退一步,弄清楚訪問數(shù)據(jù)是從哪來的?
我們?cè)诋a(chǎn)品的不同部分使用實(shí)時(shí)訪問量來顯示每段內(nèi)容的查看情況。無論什么時(shí)候有人在Quora上查看了內(nèi)容的某個(gè)片段,例如一個(gè)答案,我都能對(duì)訪問量加1,并且我們想在持久化存儲(chǔ)中保存所有的數(shù)據(jù),因?yàn)椴幌肱獊G任何數(shù)據(jù)。一種比較天真的實(shí)現(xiàn)方式是通過如下的方式來記錄訪問量:
# # 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)
這看起來相當(dāng)簡單,對(duì)吧?如果系統(tǒng)只需要處理每秒 10 次的訪問量,也就是說每 100 毫秒增加1次訪問,那這是相當(dāng)合理的。但是,如果訪問次數(shù)是每秒100次?1000次?甚至更多?如果系統(tǒng)中有成千上萬的答案,并且這些答案都會(huì)源源不斷的獲取到新的流量,系統(tǒng)將會(huì)是什么樣子?
對(duì)于一個(gè)產(chǎn)品來說,訪問是最頻繁的操作(基本上每個(gè)頁面的加載都會(huì)有),如果持久化每個(gè)行為將會(huì)對(duì)落地真實(shí)數(shù)據(jù)(ground-truth data)存儲(chǔ)(HBase)產(chǎn)生太多的寫操作,從而無法滿足可擴(kuò)展性問題。一種更好的可擴(kuò)展方案是使用事件隊(duì)列,然后按一定的規(guī)則進(jìn)行批量增加。
# # 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)
對(duì)于這種設(shè)計(jì),訪問數(shù)據(jù)將先進(jìn)入一個(gè)簡單的持久化事件隊(duì)列(在Redis上實(shí)現(xiàn)的)。有了事件隊(duì)列,我們可以有效的分?jǐn)?I/O 延遲,這樣可以大幅度提高系統(tǒng)的吞吐量。從產(chǎn)品的角度來看,如果我們展示訪問次數(shù)的頻率比批量操作頻率更高,則實(shí)時(shí)的訪問量可以通過***獲取落地真實(shí)數(shù)據(jù),此后通過對(duì)Redis緩存中的數(shù)據(jù)進(jìn)行遞增來實(shí)現(xiàn)。下面是該流程的簡化圖:
聚合訪問量到 MVWs
現(xiàn)在,我們已經(jīng)在某個(gè)地方有了實(shí)時(shí)訪問數(shù)據(jù),現(xiàn)在我們?cè)囍谶@上面創(chuàng)建 MVW。
通過產(chǎn)品定義,一個(gè)最重要的步驟是通過每個(gè)答案的訪問量來為每個(gè)用戶聚合每個(gè)主題的訪問量。這種聚合需要花費(fèi)大量的計(jì)算來整合多個(gè)數(shù)據(jù)源,包括答案的訪問次數(shù),問題-主題之間的關(guān)系,問題-答案之間的關(guān)系。
此外,任何時(shí)候如果有答案被刪除,都會(huì)引起級(jí)聯(lián)效果。已刪除答案的訪問次數(shù)不應(yīng)該包含在與此相關(guān)主題的用戶排名范圍以內(nèi),這就意味著我們需要對(duì)這些主題的排名進(jìn)行更新。同樣,如果某個(gè)答案出現(xiàn)了質(zhì)量問題,也會(huì)出現(xiàn)類似的情況。這樣就會(huì)導(dǎo)致更多的計(jì)算和聚合。
并且我們也開始注意到,Quora系統(tǒng)許多已存在的功能也會(huì)影響 MVW 的設(shè)計(jì)。下面是計(jì)算 MVW 數(shù)據(jù)所需要的實(shí)體之間的整體依賴關(guān)系圖:
用戶期望在訪問 MVW 頁面時(shí)能夠快速的加載。但是,聚合操作很慢,并且頁面加載時(shí)計(jì)算機(jī)之間的通信也無法滿足,這些數(shù)據(jù)需要在頁面加載前緩存起來。但是,如果這些依賴的項(xiàng)一直在改變,我們就需要不停的重新計(jì)算才能緩存。
關(guān)于訪問頻率的記錄我們已在上面探討過,如果任何訪問量的改變都要反應(yīng)到排名,我們就需要進(jìn)行大量的重新聚合操作。我們當(dāng)然可以投入更多的技術(shù)資源來支持這種復(fù)雜的場(chǎng)景,但是在這之前,我們考慮這是否真的是產(chǎn)品的需求?
在那天快結(jié)束的時(shí)候,我們的目標(biāo)不僅僅是解決一個(gè)困難的工程問題,而是通過一個(gè)好的產(chǎn)品特性來實(shí)現(xiàn)***用戶體驗(yàn)?;氐皆嫉哪繕?biāo),即識(shí)別每個(gè)主題的優(yōu)秀作者,我們確定的是在一個(gè)主題有新的 MVW 時(shí),需要通知用戶。如果排名非常不穩(wěn)定,每秒都有可能變化,一個(gè)新的 MVW 可能剛接到通知到達(dá)前10,打開時(shí)才發(fā)現(xiàn),數(shù)據(jù)已經(jīng)過時(shí)了。
經(jīng)過工程師、設(shè)計(jì)師、數(shù)據(jù)科學(xué)家及產(chǎn)品經(jīng)理的討論,我們覺得這樣頻繁的重新計(jì)算如此復(fù)雜的聚合是一個(gè)非常不好的主意,因?yàn)椋?/p>
1、實(shí)時(shí)排名引起技術(shù)點(diǎn)復(fù)雜性。上圖中任何依賴的改變(如,有訪問行為時(shí)或者答案被刪除時(shí))都會(huì)觸發(fā)排名的改變。
2、從可用性角度考慮,實(shí)時(shí)排名也是非常不好的。不穩(wěn)定的排名會(huì)引起用戶的混亂。
我們已經(jīng)確定,對(duì)所有狀態(tài)改變敏感的排名不是產(chǎn)品的需求(實(shí)際上也不是所期望的)。相反,一種更好的選擇是周期性預(yù)聚合 MVWs 的離散版本,而不是像上面的流程一樣,依賴實(shí)時(shí)的訪問情況。我們可以通過線下的批量更新來計(jì)算下一個(gè)版本的 MVWs。
再看訪問數(shù)據(jù)流
為了支持線下的計(jì)算,我們通過第二條線下查詢的流水線來上傳訪問數(shù)據(jù)。有了這個(gè)設(shè)置,單個(gè)訪問被記錄在一個(gè)web 服務(wù)器的訂閱(Scribe)上,這樣通過多階段線下數(shù)據(jù)流,我們可以將數(shù)據(jù)上傳到 Amazon S3,并最終導(dǎo)入到 Redshift。我們之所以選擇 Redshift,是因?yàn)樵谶@上面做聚合操作非常簡單,很適合大規(guī)模復(fù)雜查詢。除了訪問數(shù)據(jù),其他相關(guān)的數(shù)據(jù)也可以上傳到 Redshift。
該流水線可以抽象為下圖所示:
***訪問作者,從開始到完成:
現(xiàn)在,有了批量更新 MVWs 的所有數(shù)據(jù),讓我們走一遍真正計(jì)算排名的流程。
在討論最終的架構(gòu)之前,先重新看一下我們想檢索的信息:
1、給定一個(gè)主題,返回最近 30 天內(nèi)按答案訪問量排名的 MVWs 列表。
2、給定一個(gè)用戶,返回該用戶屬于 MVW 的主題列表。
3、給定一個(gè)主題,返回新的 MVWs 用戶列表,并給他們發(fā)送通知。
我們選擇使用HBase(一個(gè)開源的,非關(guān)系型的,基于 Google's BigTable的分布式數(shù)據(jù)庫)作為我們主要的后端來持久化存儲(chǔ) MVW 的緩存數(shù)據(jù)。一個(gè)好處是使用HBase能獲得比關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(RDBMS),如 MySQL,或者是簡單的內(nèi)存緩存,如Redis,更好的大數(shù)據(jù)量的讀寫性能。由于所有的 MVW 數(shù)據(jù)都是預(yù)聚合的,所以只需要簡單的key-value查詢,而不要求范圍或聚合查詢。因此,我們的使用場(chǎng)景不涉及到需要關(guān)系型數(shù)據(jù)庫索引的復(fù)雜查詢。
例如,我們想緩存一些預(yù)聚合數(shù)據(jù)來支持如下1—10排名的查詢:
(Topic, Rank) -> (User, TopicViewCount)
我們可以創(chuàng)建一個(gè)關(guān)鍵字為“topic”和“rank”的HBase表格,每一行需要一個(gè)“user”列和一個(gè)“topic view count”列。HBase的另一個(gè)非常好的特性是,我們實(shí)際上可以定義一個(gè)“user”的列族和一個(gè)“topic view count”列族,每個(gè)包含一系列的版本值。通過每個(gè)列族,HBase允許動(dòng)態(tài)的聚合新的列。每次周期性更新計(jì)算出來的新版本排名時(shí),我們都可以創(chuàng)建一個(gè)新的列來存儲(chǔ),通過直接指定這些列的存活時(shí)間,老版本就會(huì)自動(dòng)過期。
回顧一下,我們記錄訪問數(shù)據(jù)到線下的數(shù)據(jù)流(Redshift)中,預(yù)聚合每個(gè)主題的排名,并在HBase中緩存結(jié)果,在最終的 MVW 功能中能夠高效的查詢數(shù)據(jù)。下面這個(gè)圖總結(jié)了從訪問記錄到 MVW 數(shù)據(jù)的聚合到如何使用這些數(shù)據(jù)的流程:
在Quora的產(chǎn)品工程中的教訓(xùn)
通過創(chuàng)建 MVWs 的過程,我學(xué)到了許多關(guān)于產(chǎn)品工程的有效方式。該項(xiàng)目的成功主要?dú)w結(jié)為對(duì)如下原則的堅(jiān)持:
1、明確定義技術(shù)和功能目標(biāo)。例如,通過清晰的映射產(chǎn)品功能目標(biāo)與完整的依賴圖(如上),我們能夠避免出現(xiàn)因很小的改變而引起的極度不穩(wěn)定排名,并且這種排名對(duì)用戶體驗(yàn)沒有任何價(jià)值。這有助于我們做出正確的權(quán)衡,實(shí)現(xiàn)一個(gè)基于版本的設(shè)計(jì),這既有利于技術(shù)層面的擴(kuò)展,也有利于用戶端的直觀感受。
2、支持簡潔性和可擴(kuò)展性。如果有疑問,寧可降低復(fù)雜性。從產(chǎn)品角度來說,這可以給用戶提供一個(gè)更清晰易懂的排名功能,從技術(shù)的角度來說,為我們?cè)谝粋€(gè)簡單易懂的系統(tǒng)上持續(xù)迭代打開了一扇大門。
總之,作為一個(gè)產(chǎn)品工程師,我已經(jīng)能夠更好的看到 MVW 的影響,我也期待在未來能做更多的改進(jìn)。對(duì)創(chuàng)建Quora的下一個(gè)精彩的功能有興趣嗎?查看職業(yè)生涯頁獲取更多關(guān)于在這里作為產(chǎn)品工程師的感受!