分佈式緩存技術redis系列(二)詳細講解redis數據結構(內存模型)以及常用命令

java那些事2018-12-15 12:45:03

作者:ITPSC

鏈接:www.cnblogs.com/hjwublog

Redis

與Memcached僅支持簡單的key-value結構的數據記錄不同,Redis支持的數據類型要豐富得多,常用的數據類型主要有五種:String、List、Hash、Set和Sorted Set。

Redis數據類型內存結構分析

Redis內部使用一個redisObject對象來表示所有的key和value。redisObject主要的信息包括數據類型(type)、編碼方式(encoding)、數據指針(ptr)、虛擬內存(vm)等。type代表一個value對象具體是何種數據類型,encoding是不同數據類型在redis內部式。

redisObject 對象示意圖

下面分別介紹5種數據類型的用法。

String類型

字符串是Redis值的最基礎的類型。Redis中使用的字符串是通過包裝的,基於c語言字符數組實現的簡單動態字符串(simple dynamic string, SDS)一個抽象數據結構。其源碼定義如下:

struct sdshdr {

    int len; //len表示buf中的字符串的長度。

    int free//free表示buf中空閒空間的長度。

    char buf[]; //buf用於存儲字符串內容。

};


C語言字符串內存結構示意圖1

假設上圖是”hello”字符串的內存結構,這個時候len=5,free=2那麼redis包裝後(sds)其長度為:

sizeof(struct sdshdr) + len + free + 1

其中buf的大小為:

len + free + 1

1表示1個字節是用來存儲結束符’\0’的。Redis字符串是二進制安全的,因為二進制數據通常會有中間某個字節存儲’\0’的這種情況,這意味著一個Redis字符串可以包含任何種類的數據,例如一個JPEG圖像或者一個序列化的Ruby對象。二進制是否安全,簡單的理解就是能不能在字符串中間有‘\0’,如下圖:

C語言字符串內存結構示意圖2

對於上圖,sds認為這個字符串是“hello world”,而C語言的字符處理函數認為這個字符串是“hello”。

應用場景

String是最常用的一種數據類型,普通的key/value存儲都可以歸為此類。

常用命令

(1)set——設置key對應的值為String類型的value

(2)get——獲取key對應的值

192.168.2.129:6379setnx name lisi

(integer) 0

192.168.2.129:6379setnx name1 wangwu

(integer) 1

192.168.2.129:6379get name

"zhangsan"

192.168.2.129:6379get name1

"wangwu"

192.168.2.129:6379>

(3)mget——批量獲取多個key的值,如果可以不存在則返回nil

192.168.2.129:6379mget name name1

1) "zhangsan"

2) "wangwu"

192.168.2.129:6379mget name name1 name2

1) "zhangsan"

2) "wangwu"

3) (nil)

192.168.2.129:6379>

(4)incr && incrby——incr對key對應的值進行加加操作,並返回新的值;incrby加指定值

192.168.2.129:6379get age

"20"

192.168.2.129:6379incr age

(integer) 21

192.168.2.129:6379set age1 "20"

OK

192.168.2.129:6379get age1

"20"

192.168.2.129:6379incr age1

(integer) 21

192.168.2.129:6379incrby age 3

(integer) 24

從上面的結果可以看出,我們對int型的age和string型的age1都能進行incr操作時,

實際上type=string代表value存儲的是一個普通字符串,那麼對應的encoding可以是raw或者是int,如果是int則代表實際redis內部是按數值型類存儲和表示這個字符串的,當然前提是這個字符串本身可以用數值表示,比如"20"這樣的字符串,當遇到incr、decr等操作時會轉成數值型進行計算,此時redisObject的encoding字段為int。如果你試圖對name進行incr操作則報錯。

192.168.2.129:6379> incr name

(error) ERR value is not an integer or out of range

(5)decr && decrby——decr對key對應的值進行減減操作,並返回新的值;decrby減指定值

192.168.2.129:6379decr age

(integer) 23

192.168.2.129:6379decrby age 3

(integer) 20

192.168.2.129:6379>

(6)其他命令

Hash類型

Hash是一個String類型的field和value之間的映射表,即redis的Hash數據類型的key(hash表名稱)對應的value實際的內部存儲結構為一個HashMap,因此Hash特別適合存儲對象。相對於把一個對象的每個屬性存儲為String類型,將整個對象存儲在Hash類型中會佔用更少內存。

Hash 數據類型內部結構示意圖

當前HashMap的實現有兩種方式:當HashMap的成員比較少時Redis為了節省內存會採用類似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,這時對應的value的redisObject的encoding為zipmap,當成員數量增大時會自動轉成真正的HashMap,此時encoding為ht。

應用場景

用一個對象來存儲用戶信息,商品信息,訂單信息等等。

常用命令

(1)hset——設置key對應的HashMap中的field的value

(2)hget——獲取key對應的HashMap中的field的value

192.168.2.129:6379hset myhash name zhangsan

(integer) 1

192.168.2.129:6379hset myhash age 20

(integer) 1

192.168.2.129:6379hget myhash name

"zhangsan"

192.168.2.129:6379hget myhash age

"20"

192.168.2.129:6379>

(3)hgetall——獲取key對應的HashMap中的所有field的value

192.168.2.129:6379hgetall myhash

1) "name"

2) "zhangsan"

3) "age"

4) "20"

192.168.2.129:6379>

(4)其它命令

List類型

Redis的List類型其實就是每一個元素都是String類型的雙向鏈表。我們可以從鏈表的頭部和尾部添加或者刪除元素。這樣的List既可以作為棧,也可以作為隊列使用。

List數據結構內部示意圖

應用場景

如好友列表,粉絲列表,消息隊列,最新消息排行等。

常用命令

(1)lpush——在key對應的list的頭部

(2)lrange——獲取key對應的list的指定下標範圍的元素,-1表示獲取所有元素。

(3)lpop——從key對應的list的尾部刪除一個元素,並返回該元素。

192.168.2.129:6379lpush newlist news1 news2 news3

(integer) 3

192.168.2.129:6379lrange newlist 0 -1

1) "news3"

2) "news2"

3) "news1"

192.168.2.129:6379lpop newlist

"news3"

192.168.2.129:6379lrange newlist 0 -1

1) "news2"

2) "news1"

192.168.2.129:6379>

從上面的操作可以看出,lpush、lpop從表頭操作。

(4)rpush——在key對應的list的尾部添加一個元素。

(5)rpop——從key對應的list的尾部刪除一個元素,並返回該元素。

192.168.2.129:6379rpush newlist2 news1 news2 news3

(integer) 3

192.168.2.129:6379lrange newlist2 0 -1

1) "news1"

2) "news2"

3) "news3"

192.168.2.129:6379rpop newlist2

"news3"

192.168.2.129:6379>

從上面的操作可以看出,rpush、rpop從表尾操作

(6)其他命令

Set類型

Redis 集合(Set類型)是一個無序的String類型數據的集合,類似List的一個列表,與List不同的是Set不能有重複的數據。實際上,Set的內部是用HashMap實現的,Set只用了HashMap的key列來存儲對象。我們來看看java中HashSet的源碼:

public class HashSet<E>
    extends AbstractSet<E>

    implements Set<E>, Cloneablejava.io.Serializable

{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap map;

    // Dummy value to associate with an Object in the backing Map

    private static final Object PRESENT = new Object();


    /**
     * Constructs a new, empty set; the backing HashMap instance has
     * default initial capacity (16) and load factor (0.75).
     */

   public HashSet() {
        map = new HashMap<>();
}

......



/**
     * Returns an iterator over the elements in this set.  The elements
     * are returned in no particular order.
     *
     * @return an Iterator over the elements in this set
     * @see ConcurrentModificationException
     */


    public Iterator iterator() {
        return map.keySet().iterator();
    }

可見創建一個HashSet的時候實際上創建了一個HashMap;Set中的元素,只是存放在了底層HashMap的key上,底層HashMap的value列為空,遍歷HashSet的時候從HashMap中取出keySet來遍歷。

Set底層結構示意圖

應用場景

集合有取交集、並集、差集等操作,因此可以求共同好友、共同興趣、分類標籤等。

常用命令

(1)sadd——在key對應的set中添加一個元素。

(2)smembers——獲取key對應的set的所有元素。

(3)spop——隨機返回並刪除key對應的set中的一個元素。

192.168.2.129:6379sadd myset news1 news2 news3

(integer) 3

192.168.2.129:6379smembers myset

1) "news3"

2) "news2"

3) "news1"

192.168.2.129:6379spop myset

"news3"

192.168.2.129:6379>

(4)sdiff——求給定key對應的set與第一個key對應的set的差集

192.168.2.129:6379smembers myset

1) "news3"

2) "news2"

3) "news1"

192.168.2.129:6379sadd myset2 news3 news4 news5

(integer) 3

192.168.2.129:6379smembers myset2

1) "news4"

2) "news3"

3) "news5"

192.168.2.129:6379sdiff myset myset2

1) "news1"

2) "news2"

192.168.2.129:6379>

(5)suion——求給定key對應的set並集

192.168.2.129:6379> sunion myset myset2

1"news3"

2"news1"

3"news2"

4"news4"

5"news5"

192.168.2.129:6379>

(6)sinter——求給定key對應的set交集

192.168.2.129:6379sinter myset myset2

1) "news3"

192.168.2.129:6379>

(7)其他命令

SortSet

SortSet顧名思義,是一個排好序的Set,它在Set的基礎上增加了一個順序屬性score,這個屬性在添加修改元素時可以指定,每次指定後,SortSet會自動重新按新的值排序。

sorted set的內部使用HashMap和跳躍表(SkipList)來保證數據的存儲和有序,HashMap裡放的是成員到score的映射,而跳躍表裡存放的是所有的成員,排序依據是HashMap裡存的score。

應用場景

如按時間排序的時間軸。

常用命令

(1)zadd ——在key對應的zset中添加一個元素

(2)zrange——獲取key對應的zset中指定範圍的元素,-1表示獲取所有元素

192.168.2.129:6379> zadd myzset 1 "one" 2 "two" 3 "three"

(integer) 3

192.168.2.129:6379> zrange myzset 0 -1

1"one"

2"two"

3"three"

192.168.2.129:6379> zrange myzset 0 -1 withscores

1"one"

2"1"

3"two"

4"2"

5"three"

6"3"

192.168.2.129:6379>

(3)zrem——刪除key對應的zset中的一個元素

192.168.2.129:6379zrem myzset one

(integer) 1

192.168.2.129:6379zrange myzset 0 -1 withscores

1) "two"

2) "2"

3) "three"

4) "3"

192.168.2.129:6379>

(4)其它命令 

Redis常用命令

鍵值常用命令

keys/exits/del/expire/ttl/move/persist/randomkey/rename/type

服務器常用命令

ping/echo/select/quit/dbsize/info/config get/flushdb/flushall

這些命令都很容易使用,就不舉例說明了。到此,redis的數據類型以及常用命令已經介紹完畢,下一篇我們將學習redis的一些高級特性。

參考文檔

http://www.redis.cn/documentation.html
http://blog.csdn.net/tonysz126/article/details/8280696/

推薦程序員必備微信號 


程序員內參
微信號:

programmer0001



推薦理由:
在這裡,我們分享程序員相關技術,職場生活,行業熱點資訊。不定期還會分享IT趣文和趣圖。這裡屬於我們程序員自己的生活,工作和娛樂空間。


 ▼長按下方↓↓↓二維碼識別關注
閱讀原文

TAGS: