[百度空間搬家] 2013-03-16 從redis談數據結構

    說到redis,最近可是挺火的呀,越來越多的互聯網公司都開始使用了,所以我最近也研究了一下,順帶把我的理解寫下來,如果有什麼問題的話,請指正.

 
首先redis相比memcache一個很不一樣的一點就是支持一些複雜的數據結構,比如list,set ,sorted set,hash等,所以我們就先從這些數據結構入手,進行講解
 
1.list 列表
這個是一個非常常見的數據,相應的實現也有多種,比較常見的是基於數組和基於鏈表的(比如java中的ArrayList和LinkedList)
那我們先了解到最開始的兩個數據結構了
數組:別告訴我這個你不懂 ,new String[3]這樣的用過吧??這個結構最簡單了
這個結構有個好處就是通過索引來定位數據非常快,但是呢,增加,刪除數據的時候就麻煩了,需要移動大量的指針,就好比排隊買棗糕王,前面的人買好走了,那是不是大家都要移動??而且我們查找人的時候經常還會需要根據具體的值來查找,比如排隊的時候,我們要找具體某個人,我們一般不會知道它在隊列中的索引,而只知道TA的名字,這個時候,就需要遍歷整個隊列來尋找了(衆人:你不會叫名字呀???不是一下就找到了?? 我: -_-!!! 數組中的數據也這麼智能的話可以考慮...)
於是,下面這位同學登場了
鏈表:爲了簡單,我就拿單向鏈表舉例,大概就是長這個樣子(一圖勝千言):
      

 
 
     上面這個圖不知道大家看懂沒(畫圖是我的硬傷.....),不過這個鏈表相比數組肯定是複雜了一些,但是也帶來了一些好處,就是我們修改的時候方便了,在我們增加和刪除節點的時候,只需要改一下鏈接就行了,時間複雜度爲o(1) .什麼??你不懂啥叫時間複雜度... 
     好吧,我簡單介紹下啥叫時間複雜度,首先時間複雜度一般涉及查找和修改(添加,刪除)的時間複雜度.簡單你可以理解爲CPU在處理你的查找或者修改的時候需要執行的時間度量.舉個例子吧,比如剛纔的數組,你如果要根據值查找的話,需要遍歷整個數組(假設數組長度爲n)來尋找,那就是讓CPU去運行n次對比的操作(雖然實際對比次數<=n),那我們就說查找的時間複雜度是o(n),插入和刪除的時候我們也可能移動n個數據的位置,所以修改的時間複雜度也是o(n),也就是說,這裏的時間複雜度只是一個大體的估計,或者說叫一個時間的數量級吧.
     瞭解了時間複雜度,就可以知道鏈表增加和刪除節點的代價是很小的,但是,查找數據怎麼辦呢??還是o(n)的複雜度呀?這個問題等下我再回答,因爲這裏討論的是list的實現,用這裏的知識就可以了
     redis中list的實現有兩種,一種是ziplist,還有一種就是linkedlist,ziplist我就不介紹了,就是linkedlist的一種優化,這裏主要講解的是linkedlist,這裏使用的是雙向鏈表,和單向鏈表不同的地方就是多了一個指針指向前一個節點,這樣可以方便的進行逆向的遍歷
     既然list是用雙向鏈表實現的,那就會有鏈表的特點,可以看到lpush的時間複雜度爲o(1),而查找比較慢,比如根據數值刪除的操作lrem複雜度爲o(n)
 
2.set 無序集合
     說到set,在java中,大家用的最多的應該是hashset吧,其實hashset就是更加常用的hashmap實現的,而redis中的set實現有兩種,一種是intset,還有一種是hashtable實現的set,intset算是一種特定的優化版本,在數據量小,並且set中是數值的時候,會使用,如果超過了一定量(set_max_intset_entries參數指定,默認512),就會變成hashtable版本的,而intset實際上就是普通的數組結構,所以接下來主要介紹的還是hashtable的實現
     hashtable(哈希表)就是爲了解決我們剛纔說的查找數據慢產生的,大體上是由hash算法 + 數組 + 鏈表實現的(聽起來就感覺比較強大吧^^),簡單的說,就是把需要存儲的數據先均勻的進行分組(hash + 數組),之後,數組中存儲的元素是一個鏈表,鏈表中將相同hash值的元素串起來,這樣在進行搜索的時候,就可以通過hash迅速的定位到數據了,所以我們看到sismember的時間複雜度是o(1),而且由於元素以鏈表進行組織,修改的時間複雜度也是o(1),哈哈,多麼強大的數據結構呀,這似乎已經完美了......但是,我們忘了重要的一點,它是無序的,所以我們還需要下面這個數據結構來做支撐.
 
3.sorted set 有序集合
     說到排序,這個也算是算法中很重要的一部分了,還記得那些冒泡,快速,選擇排序等麼,這些都是爲了提高排序的速度產生的,但是它們是基於已經存在的數組,而我們這裏的排序是數據在進行修改的時候進行的,也就是說,你添加和刪除元素的時候,就保證了數據是有序的了,而且你還需要保證查找的速度夠快,這個可不是一個簡單的問題,爲了讓這幾個條件都滿足,各種數據結構都出現了(所以也就沒那麼簡單了),比如數據庫和文件系統中常用的B+tree,還有常見的平衡二叉樹--紅黑樹,還有我們就是redis的sorted set的實現-- skiplist,也許你會問爲啥不用紅黑樹,我覺得是因爲實現難易程度的原因,skiplist實現起來比較簡單,而且各方面性能和紅黑樹差不多,查找,修改的複雜度都爲log(n) (指數爲2).
     redis中的sorted set其實並不是簡單的由skiplist實現的,它還使用了一個hashtable來存儲 數據與score的映射(這裏提一個疑問,不知道爲啥,redis的作者把zskiplist的定義寫在了redis.h中,而沒有單獨放到一個文件裏),實際上skiplist中已經存儲了score了,把數據做冗餘應該是爲了提高查詢的速度.
     
     今天就寫到這裏吧,其實之前總是覺得java程序員好像不需要學習數據結構,算法這些吧,但是最近漸漸的發現,其實這些都是基礎,不管是學習什麼語言都應該掌握,可能我們不會自己去實現這些數據結構和算法,但是瞭解了這些,你才能更好的去使用基於這些基礎搭建起來的軟件,才能使用的更加得心應手^^
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章