redis存儲微博點贊的人,如何存儲?
問題描述
比如說有一個微博的TID是1。 UID為1,2,3,4,5,6,7,8,9的用戶都給這個微博點贊了。用redis緩存框架存儲的話如何存儲。微博可能有幾十萬個。如果用
key->set(value) 這種形式的話 key是微博ID的標(biāo)示 value是 [1,2,3,4,5,6,7,8,9]這種形式,這樣的話有多少個微博就有多少個K-V存儲。我想知道這樣會有什么弊端嗎?或者有什么更好的方法嗎?
問題解答
回答1:可以采用多個HashSet存儲。每一條微博只是HashSet內(nèi)的一個子key。增加贊的數(shù)量可以采用HIncrBy命令。把TID分塊,使得每個HashSet內(nèi)的key不超過100個。官方文檔表示HashSet在內(nèi)部元素小于一百個的時候采用線性存儲與掃描,跟同數(shù)據(jù)規(guī)模下樹形結(jié)構(gòu)相比效率更高,更省內(nèi)存。
例如:TID為123456的微博存在z:1234的HashSet中,其key為56。假設(shè)最新的微博活躍程度也較高,那么絕大多數(shù)情況下被調(diào)用的HashSet只有區(qū)區(qū)幾個,對CPU的緩存很友好。
如果要管理贊的用戶,可以自定義數(shù)據(jù)格式。在用戶數(shù)量少的時候把用戶列表整個內(nèi)嵌到HashSet的值域中。用戶超過比如50人后將其單獨(dú)整理出一個Set,在HashSet里保存Set的key。例:
bash# 內(nèi)嵌UID的情況hget z:1234 56> '1,2,3,4'...# 使用set的情況hget z:1234 56> 'UIDlist:10'smembers UIDlist:10> 1) '1'> 2) '2'> ...
由于絕大多數(shù)微博贊的用戶均比較少,HashSet可以節(jié)省出不少全局空間的key(全局key比HashSet的key更耗內(nèi)存)。
關(guān)于 @賣掉內(nèi)褲去上網(wǎng) 的回答:若采用in-place quicksort,50個用戶的手動排序效率非常高,因為在這個數(shù)據(jù)規(guī)模下數(shù)據(jù)緊密存放帶來的緩存友好性遠(yuǎn)勝于Redis ZSet相對于手動排序帶來的提升。而贊的用戶升上去后就會自動適應(yīng)成set或者zset,保證算法時間復(fù)雜度。如果還擔(dān)心效率,可以把排序好的UID列表重新寫回HashSet的一個value中,以后沒有數(shù)據(jù)改變的話直接使用。
究竟采用set還是zset還是要看樓主的需求。set加入一個成員的復(fù)雜度為O(1),zset是O(log N),但set就沒有排序功能了。
回答2:不太推薦LS用HASH來存儲點贊的數(shù)據(jù). 因為沒辦法進(jìn)行排序(如果需要的話. 我想一定需要)
目前 我們是這樣處理的.
可以使用ZSET有序集合進(jìn)行存儲. 理論上說一個ZSET中, 10W以內(nèi)的數(shù)量并無任何鴨梨. 也就是說一條微博點贊的人數(shù)再10W以內(nèi)(這是不可能的).
php$redis->ZADD('t:$tid:liked', time(), $uid); //$tid 為你的微博ID, $uid 為你的點贊人的UID//取出點贊的人(支持按照點贊時間來排序的哦:)).. 按照LSD的說的 HASH取出來的值沒有任何順序的.$uids = $redis->ZREVRANGE('t:$tid:liked', $offset, $max, TRUE); //倒序取值$uids = $redis->ZRANGE('t:$tid:liked', $offset, $max, TRUE); //順序取值//$offset 和 $max 這樣來算$pagesize = 20;$offset = ($page > 1) ? ($page - 1) * $pagesize : 0;$max = ($page * $pagesize) - 1;//一次性取出所有的這樣取.$total = $redis->ZCARD('t:$tid:liked');$uids = $redis->ZREVANGE('t:$tid:liked', 0, $total - 1, TRUE);//拿到的$uids 是一個array 哦..//判斷一個用戶是否點贊了這一來哦$redis->ZSCORE('t:$tid:liked', $uid);//取消贊這樣來$redis->ZREM('t:$tid:liked', $uid);//批量取消某短時間內(nèi)的點贊這樣操作$redis->ZREMRANGEBYSCORE('t:$tid:liked', $start_timestamp, $end_timestamp);//諸如此類的操作, 要比HASH強(qiáng)很多.
恩再PS一下!!!
如果需要用到NOSQL這樣的數(shù)據(jù)庫來存儲類似微博的數(shù)據(jù)的話, 可以這樣存儲:).
php$pipe = $redis->MULTI(Redis::PIPELINE);$pipe->SET('t:$tid', json_encode($data)) //json這種格式存儲貌似有點廢物. 如果能想到更好的格式的話,不要用JSON, 因為JSON太大了.. 比如MSGPACK這個個是就比JSON要好很多 ->ZADD('t:scores', time(), $tid);$pipe->EXEC();//PIPE 這樣的操作贊爆了. 如果你的REDIS支持事務(wù)的話, PIPE就不是一個原子性的操作了//取出數(shù)據(jù)的話就很好取出了!! $tid = $reids->ZREVRANGE('t:scores', 0, 100);$pipe = $redis->MULTI(Redis::PIPELINE); foreach($tid as $key=> $value){ $pipe->GET('t:$value');}$list = $pipe->EXEC();//$list就是你的數(shù)據(jù)啦
再再PS一下, 微博評論也是類似的存儲方法. 只是需要約定$redis KEYS的名稱. 比如:c:<評論ID> 怎么和微博數(shù)據(jù)關(guān)聯(lián)起來可以這樣:
t:$tid:comments:scores (ZSET timestmap 評論ID);
這樣取數(shù)據(jù)的時候就用PIPELINE方便很多了.
最后啰嗦一句, NOSQL這種數(shù)據(jù)庫的 KEY 一定要設(shè)置好.
回答3:保存每個uid是否有必要,還是你覺得新浪微博就是這么搞的?大多數(shù)情況,其實大家只是關(guān)注一個數(shù)字,如果這樣的話,那么用一個數(shù)字來存儲就可以了{(lán)tid->count}
如果非要保存,建議還是用{tid->set(uid)}來保存
有個優(yōu)化就是你可以設(shè)定一個閾值,比如超過100人點贊,就不再往里面加?xùn)|西了,而僅僅加一個數(shù)字(當(dāng)然這里需要你再存一個{tid->count})。因為超過1w的微博點贊,沒人回去逐個把每個點贊的人都點開的。。
