?

基于Redis的微博系統基本功能設計

2016-11-16 13:19李磊
電腦知識與技術 2016年25期
關鍵詞:微博

李磊

摘要:微博系統對信息實時性和并發性的要求,傳統的關系型數據庫無法滿足性能要求。Key-Value模型的內存數據庫Redis,非常適合微博系統對數據快速存取的需要。

關鍵詞:Redis;Key-Value;微博

中圖分類號:TP311 文獻標識碼:A 文章編號:1009-3044(2016)25-0061-03

Abstract:Microblog system requirements for information-real-time performance and concurrency. Traditional relational database does not meet performance requirements. Key-Value model memory database Redis, very suitable for the microblog system with fast access to the data needed.

Key words:Redis; Key-Value; Microblog

1 概述

微博系統類似于一個群聊的龐大聊天室,每時每刻都會有大量的消息產生,而且產生的消息會反饋給需要的用戶,這樣就要求數據的讀寫非???。關系型數據庫在數據量超過一定規模時,由于自身邏輯相對復雜,在信息檢索上無法滿足用戶的體驗。

Redis數據庫本身的數據就放在內存中,而且有合適的數據結構。Twitter、新浪微博都是目前最大的Redis用戶。

2 Redis介紹

Redis是一個速度非??斓姆顷P系型數據庫。Redis可以存儲鍵(Key)與5種不同類型的值(Value)之間的映射,可以將存儲在內存中的鍵值對持久化到硬盤。Redis還可以使用復制特性來擴展讀性能,使用客戶端分片來擴展寫性能。

其重要特性如下:

① 持久化:Redis定期把數據異步flush到硬盤進行保存。服務器重啟,數據不會丟失。

② 主從復制:主要用1臺服務器進行數據備份與恢復。

③ Vitual Memory功能:物理內存畢竟是有限的,這技術主要是把很少用的Value保存到硬盤,而Key保留在內存做檢索,提高訪問性能。

④ 多種數據結構支持:Redis的Key是string類型,Value的類型有:string、set、list、zset(sorted set)、hash。針對每種數據類型,還提供相應的操作命令,比如list類型有LPOP、LPUSH、RPOP、RPUSH等操作,set類型SDIFF(差運算)、SINTER(交運算)、SUNION(并運算)等操作①。

3 PHP和Redis構建微博系統基本功能

利用PHP的Redis擴展,在PHP中實現微博系統新用戶的創建、消息發布、關注與粉絲設計、消息推送等基本功能。PHP版本為5.5.12,Redis版本為3.0.501。

3.1 用戶信息表示

Redis hash是一個string類型的field和value的映射表.一個key可對應多個field,一個field對應一個value。將一個對象存儲為hash類型,較于每個字段都存儲成string類型更能節省內存。新建一個hash對象時開始是用zipmap(又稱為small hash)來存儲的。這個zipmap其實并不是hash table,但是zipmap相比正常的hash實現可以節省不少hash本身需要的一些元數據存儲開銷。盡管zipmap的添加,刪除,查找都是O(n),但是由于一般對象的field數量都不太多。所以使用zipmap也是很快的,也就是說添加刪除平均還是O(1)。如果field或者value的大小超出一定限制后,Redis會在內部自動將zipmap替換成正常的hash實現.。

這里我們在數據庫中表示用戶信息和發布的消息都用Redis的hash結構。用戶信息如表1。

創建新用戶時,我們用到一個user:id:的計數器,實際就是Redis的一個Key,初始一個值,然后每次添加到user:uid的hash后值要自增1。用用戶信息中login和id兩個filed的值構造另一個hash表users:,用來建立login和id之間的映射。關鍵代碼如下:

if($redis->hget("users:",$login)){echo "{$login}已經存在,重新輸入";}

else{$uidarray=$redis->multi()->incr("user:id:")->exec();

$uid=$uidarray[0];

$userinfo=array(

"login"=>$login,

"id"=>$uid,

"name"=>$name,

"following"=>0,

"fans"=>0,

"posts"=>0,

"signup"=>time());

$redis->multi()->hset("users:",$login,$uid)

->hmset("user:{$uid}",$userinfo)->exec();}

3.2 發布的消息表示

用戶發布的消息也用hash表示,結構如表2。

發布消息時候,也用到一個計數器message:mid:,其值傳給消息中的mid,然后自增1,保證每個消息都有不同的mid。發布消息時,不僅要添加message:mid一個新的值,還要修改user:uid中的posts域的值。關鍵代碼如下:

$midarray=$redis->multi()->incr("message:mid:")->exec();

$mid=$midarray[0];

$messageinfo=array(

"content"=>$content,

"time"=>time(),

"mid"=>$mid,

"uid"=>$uid,

"login"=>$login);

$redis->multi()->hmset("message:{$mid}",$messageinfo)

->hincrby("user:{$uid}","posts",1)->exec();

3.3 用戶主頁時間線和個人時間線

Redis提供zset這種有序集合數據結構。通過zadd命令添加的成員,按照score的值排序,默認score的值遞增。在微博系統中利用該數據結構的特點,可以很方便的取出最新的消息。

用戶主頁時間是指,當用戶登錄后,能看到用戶以及用戶關注的人所發布的消息列表,這個列表以發布消息的時間排序。在Redis中用戶主頁時間線結構如表3。

用戶個人時間線僅僅只有用戶個人發布的消息列表,也是以發布時間排序。用戶個人時間線profile:uid結構如下表4。

3.4 關注者列表和粉絲列表

微博系統就是要讓用戶之間分享各自的構想、想法。當A用戶開始關注或取消關注B用戶的時候,我們不僅要更新A用戶的關注列表following:A和A用戶的個人信息中關注數量following的值,還要更新B用戶的粉絲列表fans:B和B用戶的個人信息中粉絲數量fans的值。最后把B用戶發布的消息,profile:B中的消息,更新到A用戶的主頁時間線home:A。兩個列表都用Redis的zset有序集合結構表示。表5為關注者列表結構,表6位粉絲列表結構。

開始關注操作關鍵代碼如下:

define("HOME_TIMELINE_SIZE",1000);

$fkey1="following:{$uid}";

$fkey2="fans:{$other_id}";

$have=$redis->zscore($fkey1,$other_id);

if($have==true){ echo "{$uid}已經關注{$other_id}";}

else{$time=time();

$values=$redis->multi()->zadd($fkey1,$other_id,$time)

->zadd($fkey2,$uid,$time)

->zrevrange("profile:{$other_id}",0,HOME_TIMELINE_SIZE-1,true)

->exec();

$redis->multi()->hincrby("user:{$uid}","following",$values[0])

->hincrby("user:{$other_id}","fans",$values[1])->exec();

if($values[2]){//獲取$other_id發布的消息不為空

foreach($values[2] as $key=>$value)

{ $redis->multi()->zadd("home:{$uid}",$value,$key)->exec();}}}

取消操作關鍵代碼如下:

define("HOME_TIMELINE_SIZE",1000);

$fkey1="following:{$uid}";

$fkey2="fans:{$other_id}";

$have=$redis->zrangebyscore($fkey1,$other_id,$other_id,array(true,1));

if($have==false){echo "{$uid}沒有關注{$other_id}";}

else{$values=$redis->multi()->zrem($fkey1,$other_id)

->zrem($fkey2,$uid)

->zrevrange("profile:{$other_id}",0,HOME_TIMELINE_SIZE-1,true)

->exec();

$redis->multi()->hincrby("user:{$uid}","following",-$values[0])

->hincrby("user:{$other_id}","fans",-$values[1])->exec();

if($values[2]){//獲取$other_id發布的消息不為空

foreach($values[2] as $key=>$value)

{$redis->multi()->zrem("home:{$uid}",$key)->exec();}}}

3.5 消息推送

在3.2節里面說明的是消息發布后,除了進行消息信息添加以外,用戶個人信息中發布消息數量posts值得自增。我們還應該接著把發布的消息告訴給個人時間線和主頁時間線,也就是在profile:uid(uid為發布消息的用戶id)中添加消息編號mid和時間戳time,并且在home:uid(uid為發布消息的用戶id)中也添加消息編號和時間戳time,這個時間戳應該是消息發布的時候服務器的時間戳。然后還要在粉絲的主頁時間線home:uid(發布消息用戶的粉絲id)中添加同樣的數據。由于微博系統中有的用戶粉絲數量非常大,如果同步更新可能會導致用戶長時間等待。所以,在更新的時候,可以先更新fans:uid(uid為發布消息的用戶id)集合中前面1000個關注者,對每個關注者的home:uid進行更新。關鍵代碼如下:

$redis->multi()->zadd("profile:{$uid}",$time,$mid)

->zadd("home:{$uid}",$time,$mid)->exec();

$fans=$redis->zrevrange("fans:{$uid}",0,1000,true);

foreach($fans as $key=>$value)

{ $redis->zadd("home:{$value}",$time,$mid);}

如果存在超過1000個用戶的情況,可以設計一個延遲功能來進行轉發,避免發布消息的用戶長時間等待。

4 總結和展望

Redis本身提供了很多的數據結構,靈活應用可以構造適合微博系統的數據庫。這里我們搭建了php+redis環境,做一個簡單的微博系統,實現基本功能。要開發像Twitter、sina微博等系統,還要考慮更復雜的數據構造實現更多的功能,以及如何擴展服務器來提高服務質量。

參考文獻:

[1] 唐誠.Redis數據庫在微博系統中的實踐[J].廈門城市學院學報,2012,14(3):55-59.

[2] Josianh L Carlson.Redis實戰[M].北京:人民郵電出版社,2015.

[3] 王艷,董麗麗.NoSql與關系數據庫相結合的設計與實踐[J].電腦知識與技術, 2014(9).

猜你喜歡
微博
何以解憂?基于社交媒體大數據的睡眠健康公眾敘事研究
微博信息傳播存在的主要問題和對策研究
官方微博輿論引導方式探究
打造醫院里的“主流媒體”
事實與流言的博弈
重大突發事件中微博之力不微
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合