2012-10-07 64 views
4

我正在重新實施應用程序以支持國家工程比賽,將其從本地服務器移至雲端。在網絡應用程序中提供快速`select count(*)`功能

爲了告訴球隊拔地而起的那一刻,查詢的形式

select 1 + count(*) from team where where score < ? 

球隊的比分變化非常動態的。可能有多達200萬個團隊,我需要每秒處理至少10個這樣的查詢。

通過使用團隊/成績記錄的單獨Berkeley數據庫,原始版本獲得所需的性能(實際上它已在1999年的硬件中完成)。 Berkeley DB中有一個「記錄號」功能,它提供了正確的功能,而且速度非常快。

Heroku顯然無法支持Berkeley DB。 PostgreSQL,他們的標準數據庫,select count(*)帶有全表或索引掃描,速度太慢。

關於如何進行的任何想法?我並不喜歡Heroku,但必須轉向某種雲解決方案。

回答

2

使用redis將您的團隊數據存儲在sorted set中。然後ZRANK函數將返回你需要的計數。 Redis總體來說速度非常快,並且ZRANK函數的預期值爲O(log N)。它使用跳過列表來實現。

+0

謝謝。我認爲redis是我需要的,但是我將編輯你的答案來表達它是如何真正用來解決我的問題的。感謝指針! – Gene

+0

聽起來對我很好!排序集合的工作,但我會用最初發布的INCR。在redis中建模數據並不少見,例如:每個團隊都有一個redis密鑰,例如:INCR團隊:,GET團隊:等 – hgmnz

+0

我看不到如何用INCR解決問題。我需要存儲多達10^6雙,然後能夠查詢「多少球隊得分低於X?」這些查詢發生時,這些對不斷變化。 – Gene

0

將一個索引放在分數上應該避免全表掃描。

+0

是的,但是OP已經聲明索引掃描太慢了。 – dbenham

+0

它仍在掃描索引,這太慢了。我很確定O(log n)性能是需要的。這就是伯克利DB提供的。 – Gene

+0

希望PostgresSQL可以保留索引中的元數據,比如位置號碼,或者b樹的每個分支下有多少個節點。 –

2

創建一個排名表並更新頻率足夠。包括類別(開或官員)和得分,所以你不必在查詢時將其加入到團隊表:

create table "rank" (
    team integer primary key, 
    category integer, 
    score integer, 
    rank_consolidated integer, 
    rank_category integer 
); 

begin; 
truncate table "rank" 
; 
insert into "rank" (team, category, score, rank_consolidated, rank_category) 
select 
    team, category, score, 
    rank() over(order by score desc) rank_consolidated, 
    rank() over(partition by category order by score desc) rank_category 
from team 
; 
commit 
; 
select * from "rank" where team = 11; 

至於確切的排名行爲調查window functions

+0

我很欣賞這種迴應,但我現在有一個實時解決方案(無後臺處理),保持這種方式有很多好處。如果沒有其他答案出現,將回落到此。 – Gene

+0

@Gene將成員添加,刪除或更新爲有序集會導致索引更新,其中包含相關成本。這將在Redis和Postgresql中發生。所有二進制搜索都是O(log n)。您可以像在Redis中一樣快速地完成postgresql中的排序,如果不是更快的話。如果Redis索引實現比Postgresql的btree快,我會非常驚訝。我建議後臺處理只是爲了讓它非常快速。如果您實時進行排名,它將比Redis速度更快或更快。如果球隊的數量達到200萬,則差異將顯示。 –

+0

Rdis ZRANK計算O(log N)中鍵的順序statistc(等級)。爲此,它在每個B樹節點中使用葉子計數。 (查找'訂單統計樹')我親自檢查過的Postgresql,MySQL,MSQL和Sybase需要O(N)才能獲得排名。它們不保留葉計數,因爲這些會在高度並行的應用程序中導致錯誤的鎖定行爲所以要得到一個訂單統計量(count(*),其中x <?),它們都會掃描整個索引或整個表。 – Gene

0

如果它讀從比寫入的要多得多,它總是必須是最新的,那麼這對於觸發器維護的彙總表(某種物化視圖)來說是理想的工作。

team表,AFTER EACH INSERT OR UPDATE OR DELETE FOR EACH ROW執行觸發器功能更新該團隊與新得分team_summary表項的觸發器。

team_summary表可以通過簡單的直接索引查找平等訪問,所以它會瘋狂的快。由於Pg支持同時讀寫器,即使更新非常頻繁,team_summary表也會保持響應。爲了獲得最佳效果,您唯一需要做的事情是將FILLFACTOR設置爲team_summary表中的50,以便HOT可以正常工作,並確保autovacuum設置爲經常運行以分散負載的真空I/O流失。

寫入觸發器應該非常簡單。您必須小心編寫一個併發安全觸發器,當通過多個併發連接對同一個團隊進行併發更新時,該併發安全觸發器不會中斷。喜歡的東西:

UPDATE team_summary SET score = score + 1 WHERE team_id = NEW.team_id; 

應當根據雙方SERIALIZABLEREAD COMMITTED隔離罰款。見Concurrency control。唯一的難點在於,在將新團隊的第一行插入team之前,您必須確保在team_summary中插入新行,以便您的觸發器不必處理令人驚訝的棘手情況,其中team_summary行可能尚不存在team表。爲此獲得upsert/merge權利是棘手的。

如果寫入率也非常高,而且每隔幾秒鐘/分鐘更新一次結果,則可以使用Clodoaldo的方法。

相關問題