Redis支持五中數據類型:String(字符串),Hash(哈希),List(列表),Set(集合)及zset(sortedset:有序集合)。redis
Redis定義了豐富的原語命令,能夠直接與Redis服務器交互。實際應用中,咱們不太會直接使用這些原語命令,Redis提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,大多狀況下咱們是經過各式各樣的客戶端來操做Redis。可是,任何語言的客戶端實際上都是對Redis原語命令的封裝,瞭解原語命令有助於理解客戶端的設計原理,知其然,知其因此然。數組
3.一、字符串
String是Redis最基本的數據類型,結構爲一個key對應一個value。緩存
String類型是二進制安全的,意味着能夠包含任何數據,好比jpg圖片或者序列化的對象。安全
String類型的最大能存儲512M。服務器
不像Linux有那麼多充滿想象力的命令,還喜歡帶一對莫名其妙的參數。Redis的原語命令很簡單,並且有規律可循,一句話歸納,就是乾淨利索脆。數據結構
好比咱們想設置往Redis中存放一個用戶名,用String類型存儲:函數
127.0.0.1:6379> SET name chenlongfei性能
OK 學習
「OK」是Redis返回的響應,表明設置成功。大數據
取出這個name的值:
127.0.0.1:6379> GET name
"chenlongfei"
想修改name的值爲「clf」,從新SET一遍,覆蓋掉原來的值:
127.0.0.1:6379> SET name clf
OK
127.0.0.1:6379> GET name
"clf"
想刪除該條數據:
127.0.0.1:6379> DEL name
(integer) 1 --該數字表明影響的記錄總數
127.0.0.1:6379> GET name
(nil) --nil表明爲空,不存在該對象
增刪改查命令一分鐘學會,想忘記都難,媽媽不再用擔憂個人學習!
命令格式
說明
SET key value
設置指定 key 的值
GET key
獲取指定 key 的值
SETNX key value
(Set if Not Exist)只有在 key 不存在時設置 key 的值
SETRANGE key offset value
用 value 參數覆寫給定 key 所儲存的字符串值,從偏移量 offset 開始
GETRANGE key start end
返回 key 中字符串值的子字符
GETSET key value
將給定 key 的值設爲 value ,並返回 key 的舊值
MSET key value [key value ...]
(Multi Set)同時設置一個或多個 key-value 對
MGET key1 [key2..]
獲取全部(一個或多個)給定 key 的值
APPEND key value
若是 key 已經存在而且是一個字符串, APPEND 命令將 value 追加到 key 原來的值的末尾
SETEX key seconds value
(Set Expire)將值 value 關聯到 key ,並將 key 的過時時間設爲 seconds (以秒爲單位)
PSETEX key milliseconds value
(Precise Set Expire)這個命令和 SETEX 命令類似,但它以毫秒爲單位設置 key 的生存時間,而不是像 SETEX 命令那樣,以秒爲單位
STRLEN key
返回 key 所儲存的字符串值的長度
INCR key
將 key 中儲存的數字值增一,前提是value是一個數字
INCRBY key increment
將 key 所儲存的值加上給定的增量值,前提是value是一個數字
INCRBYFLOAT key increment
將 key 所儲存的值加上給定的浮點增量值,前提是value是一個數字
DECR key
將 key 中儲存的數字值減一,前提是value是一個數字
DECRBY key decrement
key 所儲存的值減去給定的減量值,前提是value是一個數字
3.二、哈希
Redis的哈希是field和value之間的映射,即鍵值對的集合,因此特別適合用於存儲對象。
Redis 中每一個 hash 最多能夠存儲 232 - 1 鍵值對(40多億)。
例如,咱們想在Redis中存儲一個用戶信息,包括用戶ID,用戶名,郵箱地址三個字段:
127.0.0.1:6379>HMSET user_1 userId 123 userName clf email chenlongfei@163.com
OK
127.0.0.1:6379> HGETALL user_1
1) "userId"
2) "123"
3) "userName"
4) "clf"
5) "email"
6) "chenlongfei@163.com"
命令格式
說明
HMSET key field1 value1 [field2 value2... ]
(Hash Multi Set)同時將多個 field-value 對設置到哈希表 key 中
HMGET key field1 [field2...]
獲取全部給定字段的值
HSET key field value
將哈希表 key 中的字段 field 的值設爲 value
HGET key field
獲取存儲在哈希表中指定字段的值
HGETALL key
獲取在哈希表中指定 key 的全部字段和值
HDEL key field2 [field2]
刪除一個或多個哈希表字段
HSETNX key field value
只有在字段 field 不存在時,設置哈希表字段的值
HKEYS key
獲取全部哈希表中的字段
HVALS key
獲取哈希表中全部值
HEXISTS key field
查看哈希表 key 中,指定的字段是否存在
HLEN key
獲取哈希表中字段的數量
HINCRBY key field increment
爲哈希表 key 中的指定字段的整數值加上增量
HINCRBYFLOAT key field increment
爲哈希表 key 中的指定字段的浮點數值加上增量
3.三、列表
Redis列表是簡單的字符串列表,按照插入順序排序。支持添加一個元素到列表頭部(左邊)或者尾部(右邊)的操做。
一個列表最多能夠包含 232- 1 ,即超過40億個元素。
例如,咱們想用一個名爲「Continents」的列表盛放五大洲的名字:
127.0.0.1:6379> LPUSH Continents Asia Africa America Oceania Antarctica
(integer) 5
127.0.0.1:6379> LRANGE Continents 0 4 --獲取下標爲0~4的元素
1) "Antarctica"
2) "Oceania"
3) "America"
4) "Africa"
5) "Asia"
Redis列表雖然名爲列表,其實從特性上來說更像是棧,以最近放進去的元素爲頭,以最先放進去的元素爲尾,因此,Redis列表的下標呈倒序排列。上例中依次放進去的五個元素:Asia、Africa、America、Oceania、Antarctica,下標分別爲四、三、二、一、0。這與Java中List的概念徹底不同,須要特別注意。
與棧相似,當執行POP操做時,Redis列表彈出的是最新放進去的元素,相似於棧頂元素。
Redis列表還支持一種阻塞式操做,好比BLPOP(Blockd List Pop之縮寫),移出並獲取列表的第一個元素,若是列表沒有元素(或列表不存在)會阻塞列表直到等待超時或發現可彈出元素爲止。
例如,咱們對一個不存在的列表「myList」執行BLPOP命令:
BLPOPmyList 20 -- 彈出myList列表的第一個元素,若是沒有,阻塞20秒
該客戶端會進入阻塞狀態,若是20秒以內該列表存入了元素,則彈出:
27.0.0.1:6379> BLPOP myList 20 --若無元素則進入阻塞狀態,限時20秒
1) "myList"
2) "hello"
(6.20s)
若是超時後仍然沒有等到元素,則結束阻塞,返回nil:
127.0.0.1:6379> BLPOP myList 20
(nil)
(20.07s)
命令格式
說明
LPUSH key value1 [value2...]
將一個或多個值插入到列表頭部
LPOP key
移出並獲取列表的第一個元素
LPUSHX key value
(List Push if exist)將一個或多個值插入到已存在的列表頭部
LINDEX key index
經過索引獲取列表中的元素
LRANGE key start stop
獲取列表指定範圍內的元素
LSET key index value
經過索引設置列表元素的值
LTRIM key start stop
只保留指定區間內的元素,不在指定區間以內的元素都將被刪除
RPOP key
(Rear Pop)移除並獲取列表最後一個元素
RPUSH key value1 [value2...]
將一個或多個值插入到列表尾部
RPUSHX key value
將一個或多個值插入到已存在的列表尾部
LREM key count value
從列表中刪除字段值爲value的元素,刪除count的絕對值個value後結束,count > 0 從表頭刪除;count < 0 從表尾刪除;count=0 所有刪除
RPOPLPUSH source destination
移除列表的最後一個元素,並將該元素添加到另外一個列表並返回
BLPOP key1 [key2... ] timeout
移出並獲取列表的第一個元素, 若是列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止,若是timeout爲0則一直等待下去
BRPOP key1 [key2... ] timeout
移出並獲取列表的最後一個元素, 若是列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止,若是timeout爲0則一直等待下去
LINSERT key BEFORE | AFTER pivot value
在key 列表中尋找pivot,並在pivot值以前|以後插入value
LLEN key
獲取列表長度
3.四、集合
Redis集合是String類型的無序集合。集合成員是惟一的,這就意味着集合中不能出現重複的數據。
Redis集合是經過哈希表實現的,因此添加,刪除,查找的複雜度都是O(1)。
集合中最大的成員數爲 232- 1 ,即每一個集合最多可存儲40多億個成員。
集合的一大特色就是不能有重複元素,若是插入重複元素,Redis會忽略該操做:
127.0.0.1:6379> SADD direction east west south north
(integer) 4
127.0.0.1:6379> SMEMBERS direction
1) "west"
2) "east"
3) "north"
4) "south"
127.0.0.1:6379> SADD direction east
(integer) 0 --east元素已經存在,該操做無效
127.0.0.1:6379> SMEMBERS direction
1) "west"
2) "east"
3) "north"
4) "south"
Redis集合還有兩大特色,一是支持隨機獲取元素,二是支持集合間的取差集、交集與並集操做。
命令格式
說明
SADD key member1 [member2…]
向集合添加一個或多個成員
SREM key member1 [member2…]
移除集合中一個或多個成員
SPOP key
移除並返回集合中的一個隨機元素
SMEMBERS key
返回集合中的全部成員
SRANDMEMBER key [count]
返回集合中count個隨機元素,如count爲空,則只返回一個
SCARD key
(Set Cardinality)返回集合中的元素總數
SISMEMBER key member
判membe元素是不是集key 的成員
SMOVE source destination member
將member元素從source集合移動到destination集合
SDIFF key1 [key2…]
返回給定全部集合的差集,即以key1爲基準,返回key1有且[key2...]沒有的元素
SDIFFSTORE destination key1 [key2…]
返回給定全部集合的差集並存儲在destination中
SINTER key1 [key2…]
返回給定全部集合的交集
SINTERSTORE destination key1 [key2…]
返回給定全部集合的交集並存儲在destination中
SUNION key1 [key2…]
返回全部給定集合的並集
SUNIONSTORE destination key1 [key2…]
全部給定集合的並集存儲在destination集合中
3.五、有序列表
Redis 有序集合和集合同樣也是String類型元素的集合,且不容許重複的成員。
不一樣的是每一個元素都會關聯一個double類型的分數。Redis正是經過分數來爲集合中的成員進行從小到大的排序。有序集合的成員是惟一的,但分數(score)卻能夠重複。
集合是經過哈希表實現的,因此添加,刪除,查找的複雜度都是O(1)。
集合中最大的成員數爲 232- 1 ,即每一個集合最多可存儲40多億個成員。
例如,、使用有序列表來存儲學生的成績單:
127.0.0.1:6379> ZADD scoreList 82 Tom
(integer) 1
127.0.0.1:6379> ZADD scoreList 65.5 Jack
(integer) 1
127.0.0.1:6379> ZADD scoreList 43.5 Rubby
(integer) 1
127.0.0.1:6379> ZADD scoreList 99 Winner
(integer) 1
127.0.0.1:6379> ZADD scoreList 78 Linda
(integer) 1
127.0.0.1:6379> ZRANGE scoreList 0 100 WITHSCORES --獲取名次在0~100之間的記錄
1)"Rubby"
2)"43.5"
3)"Jack"
4)"65.5"
5)"Linda"
6)"78"
7)"Tom"
8)"82"
9)"Winner"
10) "99"
須要注意的是,Redis有序集合是默認升序的,score越低排名越靠前,即score越低的元素下標越小。
命令格式
說明
ZADD key score1 member1 [score2 member2 ...]
添加一個或多個成員到有序集合,或者若是它已經存在更新其分數
ZRANGE key start stop [WITHSCORES]
把集合排序後,返回名次在[start,stop]之間的元素。 WITHSCORES是把score也打印出來
ZREVRANGE key start stop [WITHSCORES]
倒序排列(分數越大排名越靠前),返回名次在[start,stop]之間的元素
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset n]
集合(升序)排序後取score在[min, max]內的元素,並跳過offset個,取出n個
ZREM key member [member ...]
從有序集合中刪除一個或多個成員
ZRANK key member
肯定member在集合中的升序名次
ZREVRANK key member
肯定member在集合中的降序名次
ZSCORE key member
獲取member的分數
ZCARD key
獲取有序集合中成員的數量
ZCOUNT key min max
計算分數在min與max之間的元素總數
ZINCRBY key increment member
給member的分數增長increment
ZREMRANGEBYRANK key start stop
移除名次在start與stop之間的元素
ZREMRANGEBYSCORE key min max
移除分數在min與max之間的元素
3.六、存儲結構
Redis的一種對象類型能夠有不一樣的存儲結構來實現,從而同時兼顧性能和內存。
字典是Redis最基礎的數據結構,一個字典即一個DB,Redis支持多DB。
Redis字典採用Hash表實現,針對碰撞問題,採用的方法爲「鏈地址法」,即將多個哈希值相同的節點串連在一塊兒,從而解決衝突問題。
「鏈地址法」的問題在於當碰撞劇烈時,性能退化嚴重,例如:當有n個數據,m個槽位,若是m=1,則整個Hash表退化爲鏈表,查詢複雜度O(n)。爲了不Hash碰撞攻擊,Redis隨機化了Hash表種子。
Redis的方案是「雙buffer」,正常流程使用一個buffer,當發現碰撞劇烈(判斷依據爲當前槽位數和Key數的對比),分配一個更大的buffer,而後逐步將數據從老的buffer遷移到新的buffer。
redisObject是真正存儲redis各類類型的結構,在Redis源碼的redis.h文件中,定義了這些結構:
/* A redisobject, that is a type able to hold a string / list / set */ /* The actualRedis Object */ #define REDIS_LRU_BITS 24 #define REDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1)/* Max value of obj->lru */ #define REDIS_LRU_CLOCK_RESOLUTION 1000/* LRU clock resolution in ms */ typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:REDIS_LRU_BITS; /* lru time(relative to server.lruclock) */ intrefcount; void*ptr; } robj;其中type即Redis支持的邏輯類型,包括:
/* Object types*/ #define REDIS_STRING 0 #define REDIS_LIST 1 #define REDIS_SET 2 #define REDIS_ZSET 3 #define REDIS_HASH 4即前面所列舉的五種數據類型。type定義的只是邏輯類型,encoding纔是物理存儲方式,一種邏輯類型可使用不一樣的存儲方式,包括:
/* Objectsencoding. Some kind of objects like Strings and Hashes can be * internally represented in multiple ways. The'encoding' field of the object * is set to one of this fields for thisobject. */ #defineREDIS_ENCODING_RAW 0 /* Rawrepresentation */ #defineREDIS_ENCODING_INT 1 /* Encoded asinteger */ #define REDIS_ENCODING_HT2 /* Encoded as hash table */ #defineREDIS_ENCODING_ZIPMAP 3 /* Encoded aszipmap */ #defineREDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */ #defineREDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ #define REDIS_ENCODING_INTSET6 /* Encoded as intset */ #defineREDIS_ENCODING_SKIPLIST 7 /* Encoded asskiplist */ #defineREDIS_ENCODING_EMBSTR 8 /* Embedded sdsstring encoding */(1) REDIS_ENCODING_RAW 即原生態的存儲結構,就是以字符串形式存儲,字符串類型在redis中用 sds( simple dynamic string)封裝,主要爲了解決長度計算和追加效率的問題。(2)REDIS_ENCODING_INT 表明整數,以long型存儲。
(3) REDIS_ENCODING_HT 表明哈希表(Hash Table),以哈希表結構存儲,與字典的實現方法一致。
(4)REDIS_ENCODING_ZIPMAP 其實質是用一個字符串數組來依次保存key和value,查詢時是依次遍列每一個key-value 對,直到查到爲止。
(5)REDIS_ENCODING_LINKEDLIST 表明鏈表,以典型的鏈表結構存儲。
(6) REDIS_ENCODING_ZIPLIST 表明一種雙端列表,且經過特殊的格式定義,壓縮內存適用,以時間換空間。ZIPLIST適合小數據量的讀場景,不適合大數據量的多寫/刪除場景。
(7) REDIS_ENCODING_INTSET 是用一個有序的整數數組來實現的。
(8)REDIS_ENCODING_SKIPLIST 同時採用字典和有序集兩種數據結構來保存數據元素。跳躍表(SkipList)是一個特殊的鏈表,相比通常的鏈表,有更高的查找效率,其效率可比擬於二叉查找樹。一張關於跳錶和跳錶搜索過程以下圖:
在圖中,須要尋找 68,在給出的查找過程當中,利用跳錶數據結構優點,只比較了 3次,橫箭頭不比較,豎箭頭比較。因而可知,跳錶預先間隔地保存了有序鏈表中的節點,從而在查找過程當中能達到相似於二分搜索的效果,而二分搜索思想就是經過比較中點數據放棄另外一半的查找,從而節省一半的查找時間。
缺點即浪費了空間,自古空間和時間兩難全。
(9)REDIS_ENCODING_EMBSTR 表明使用embstr編碼的簡單動態字符串。好處有以下幾點: embstr的建立只需分配一次內存,而raw爲兩次(一次爲sds分配對象,另外一次爲objet分配對象,embstr省去了第一次)。相對地,釋放內存的次數也由兩次變爲一次。embstr的objet和sds放在一塊兒,更好地利用緩存帶來的優點。須要注意的是,Redis並未提供任何修改embstr的方式,即embstr是隻讀的形式。對embstr的修改其實是先轉換爲raw再進行修改。
3.6.1 字符串的存儲結構
Redis的全部的key都採用字符串保存,而值能夠是字符串,列表,哈希,集合和有序集合對象的其中一種。
字符串存儲的邏輯類型即REDIS_STRING,其物理實現(enconding)能夠爲 REDIS_ENCODING_INT、 REDIS_ENCODING_EMBSTR或REDIS_ENCODING_RAW。
首先,若是可使用REDIS_ENCODING_EMBSTR編碼,Redis首選REDIS_ENCODING_EMBSTR保存;其次,若是能夠轉換,Redis會嘗試將一個字符串轉化爲Long,保存爲REDIS_ENCODING_INT,如「26」、「180」等;最後,Redis會保存爲REDIS_ENCODING_RAW,如「chenlongfei」、「Redis」等。
3.6.2 哈希的存儲結構
REDIS_HASH能夠有兩種encoding方式: REDIS_ENCODING_ZIPLIST 和 REDIS_ENCODING_HT
Hash表默認的編碼格式爲REDIS_ENCODING_ZIPLIST,在收到來自用戶的插入數據的命令時:
(1)調用hashTypeTryConversion函數檢查鍵/值的長度大於配置的hash_max_ziplist_value(默認64)
(2)調用hashTypeSet判斷節點數量大於配置的hash_max_ziplist_entries(默認512)
以上任意條件知足則將Hash表的數據結構從REDIS_ENCODING_ZIPLIST轉爲REDIS_ENCODING_HT。
3.6.3 列表的存儲結構
REDIS_SET有兩種encoding方式,REDIS_ENCODING_ZIPLIST和REDIS_ENCODING_LINKEDLIST。
列表的默認編碼格式爲REDIS_ENCODING_ZIPLIST,當知足如下條件時,編碼格式轉換爲REDIS_ENCODING_LINKEDLIST:
(1)元素大小大於list-max-ziplist-value(默認64)
(2)元素個數大於配置的list-max-ziplist-entries(默認512)
3.6.4 集合的存儲結構
REDIS_SET有兩種encoding方式: REDIS_ENCODING_INTSET 和 REDIS_ENCODING_HT。
集合的元素類型和數量決定了encoding方式,默認採用REDIS_ENCODING_INTSET ,當知足如下條件時,轉換爲REDIS_ENCODING_HT:
(1)元素類型不是整數
(2)元素個數超過配置的set-max-intset-entries(默認512)
3.6.5 有序列表的存儲結構
REDIS_ZSET有兩種encoding方式: REDIS_ENCODING_ZIPLIST(同上)和 REDIS_ENCODING_SKIPLIST。
因爲有序集合每個元素包括:<member,score>兩個屬性,爲了保證對member和score都有很好的查詢性能,REDIS_ENCODING_SKIPLIST同時採用字典和有序集兩種數據結構來保存數據元素。字典和有序集經過指針指向同一個數據節點來避免數據冗餘。
字典中使用member做爲key,score做爲value,從而保證在O(1)時間對member的查找跳躍表基於score作排序,從而保證在 O(logN) 時間內完成經過score對memer的查詢。
有序集合默認也是採用REDIS_ENCODING_ZIPLIST的實現,當知足如下條件時,轉換爲REDIS_ENCODING_SKIPLIST:
(1)數據元素個數超過配置zset_max_ziplist_entries 的值(默認值爲 128 )
(2)新添加元素的 member 的長度大於配置的zset_max_ziplist_value 的值(默認值爲 64 )
3.6.6 總結
針對同一種數據類型,Redis會根據元素類型/大小/個數採用不一樣的編碼方式,不一樣的編碼方式在內存使用效率/查詢效率上差距巨大,能夠經過配置文件調整參數來達到最優。
RedisObject
實現方式一
實現方式二
實現方式三
字符串
REDIS_ENCODING_INT
REDIS_ENCODING_EMBSTR
REDIS_ENCODING_RAW
哈希
REDIS_ENCODING_ZIPLIST
REDIS_ENCODING_HT
列表
REDIS_ENCODING_ZIPLIST
REDIS_ENCODING_LINKEDLIST
集合
REDIS_ENCODING_INTSET
REDIS_ENCODING_HT
有序集合
REDIS_ENCODING_ZIPLIST
REDIS_ENCODING_SKIPLIST