1. 程式人生 > >Redis 各種資料型別的使用場景舉例分析

Redis 各種資料型別的使用場景舉例分析

Redis官方的介紹:Redis 是完全開源免費的,遵守BSD協議,先進的key - value持久化產品。它通常被稱為資料結構伺服器,因為值(value)可以是字串(String), 雜湊(Map), 列表(list), 集合(sets) 和有序集合(sorted sets)等型別。

通過最近一直看公司的程式碼慢慢了解實際的運用和使用,記錄下各種資料型別在我們專案中的運用,結合php的陣列和mysql資料一起對比記錄。

整個專案的背景介紹,類似與豆瓣的社群,有帖子,有圖片,有相簿,有小組,有粉絲,可以評論,可以贊。

1.字串(String)

字串的功能比較簡單單一,容易理解的,類比一個數組的key-value。由於其資料結構的簡單,所以也是來做簡單的事,比如統計所有的小組數目,所有的相簿數目,因為這些資料屬於整個專案。

//使用者建立相簿的操作,以ThinkPHP的ORM為例
$data = array('name'=>'夢康的相簿','create_id'=>'1','create_time'=>time());
$res = M('ablum')->add($data);
if($res){
	//全站相簿總數加一
	$redis->incr('ablumCount');
}

那麼某個使用者的相簿總數應該怎麼存呢?

可以繼續使用String來儲存

$reids->set('user:1:ablumNum',1000)//設定id為1的使用者的相簿數為1000

上面的key的設計原則是:object-type:id:field

,從物件的角度來分析,一個使用者物件,name,ablumNum,groupNum等都是他的成員屬性,如果是從資料的角度來分析,則是user表裡面的id為1的使用者的ablum_num欄位的資料。

新增一個需求:如果需要取出相簿總數前十的使用者,怎麼排序呢?

就應該考慮使用有序集合(sorted sets),這個後面說。

2.雜湊(Map)

hashMap的使用場景,hashMap特別適合用於儲存物件。 相較於將物件的每個欄位存成單個string型別,將一個物件儲存在hash型別中會佔用更少的記憶體,並且可以更方便的存取整個物件。

使用者ID為查詢的key,儲存的value使用者物件包含姓名,年齡,生日等資訊,如果用普通的key/value結構來儲存,主要有以下2種儲存方式:

第一種方式將使用者ID作為查詢key,把其他資訊封裝成一個物件以序列化的方式儲存,這種方式的缺點是,增加了序列化/反序列化的開銷,並且在需要修改其中一項資訊時,需要把整個物件取回。

第二種方法是這個使用者資訊物件有多少成員就存成多少個key-value對兒,用使用者ID+對應屬性的名稱作為唯一標識(id:name,id:age,id:birthday)來取得對應屬性的值,雖然省去了序列化開銷和併發問題,但是一個使用者ID為重複儲存了三次,如果存在大量這樣的資料,記憶體浪費還是非常大的。

詳見:http://tech.it168.com/a2011/0818/1234/000001234478_all.shtml

3.列表(list)

redis的list型別其實就是一個每個子元素都是string型別的雙向連結串列。我們可以通過push,pop操作從連結串列的頭部或者尾部新增刪除元素。這使得list既可以用作棧,也可以用作佇列。
產品運營總會讓你展示最近、最熱、點選率最高、活躍度最高等等條件的top list。注意list有做排行榜的功能,但是list卻不方便在中間插入。如果某一個排行榜,確定是一小時跑一次,而不用中途又去人工干預,那麼就可以使用list。如果需要後期人工干預排行榜,則最好是使用zsets的結構,可以後期插入。

4.集合(sets)

集合使用的好處是快速往集合裡插入一個元素,刪除一個元素,確定一個元素是否存在於該集合。使用到的場景就是一個使用者的粉絲,資料模型就是一對多的情況,一個使用者可能有N個粉絲,所以就可以選擇集合的資料結構。

$key = "user:$user:fans";
//$fansArr 是粉絲陣列
foreach($fansArr as $fans){
	$redis->sAdd($key, $fans['id']);
}

如果某一個使用者對其取消了關注,那麼就從該集合裡刪除。如果之前選擇了list則不方便實現了

$redis->sMove($key, $fans['id']);

 

5.有序集合(zsets)

有序集合在專案中使用的最多,因為其比較方便做分頁查詢,比較容易控制順序。比如我們專案中對搜尋結果的資料就以zsets的結構來存的。因為方便排序。比如一個桌布的關鍵字會有很多圖片,以大家的點贊數來降序排列。

$key = "tagid:$id:search";
$redis->multi(Redis::PIPELINE);
foreach ($imagesArr as $image){
	//把點贊數作為權重,圖片的id作為值
    $redis->zAdd($key, $image['fav_num'], $image['id']);
}
$redis->exec();

 

比如現在桌布的搜尋結果已經存在了,但是又有一位原創使用者自己手繪的質量非常高的桌布上傳,這個時候,這個質量非常高的桌布卻沒有閱讀量,就沒有點贊數,也不會存在於搜尋列表的前面,所以需要人工干預下,給該圖片增加一些權重,放入對應的快取中,這樣就實時更新了。

以上的這些情景基本涵蓋了redis的一些比較有特色的使用情況吧。以後有新的收穫再補。