Redis數據類型及操作

Redis支持的數據類型

前言

Redis的作者antirez(Salvatore Sanfilippo)曾經發表了一篇名為Redis宣言(Redis Manifesto)的文章,文中列舉了Redis 的七個原則,以向大家闡明Redis 的思想。

  1. Redis是一個操作數據結構的語言工具,它提供基于TCP的協議以操作豐富的數據結構。在Redis中,數據結構這個詞的意義不僅表示在某種數據結構上的操作,更包括了結構本身及這些操作的時間空間復雜度。
  2. Redis定位于一個內存數據庫,正是由于內存的快速訪問特性,才使得Redis能夠有如此高的性能,才使得Redis能夠輕松處理大量復雜的數據結構,Redis會嘗試其它的存儲方面的選擇,但是永遠不會改變它是一個內存數據庫的角色。
  3. Redis使用基礎的API操作基礎的數據結構,Redis的API與數據結構一樣,都是一些最基礎的元素,你幾乎可以將任何信息交互使用此API格式表示。作者調侃說,如果有其它非人類的智能生物存在,他們也能理解Redis的API。因為它是如此的基礎。
  4. Redis有著詩一般優美的代碼,經常有一些不太了解Redis 有的人會建議Redis采用一些其它人的代碼,以實現一些Redis 未實現的功能,但這對我們來說就像是非要給《紅樓夢》接上后四十回一樣。
  5. Redis始終避免復雜化,我們認為設計一個系統的本質,就是與復雜化作戰。我們不會為了一個小功能而往源碼里添加上千行代碼,解決復雜問題的方法就是讓復雜問題永遠不要提復雜的問題。
  6. Redis支持兩個層面的API,第一個層面包含部分操作API,但它支持用于分布式環境下的Redis。第二個層面的API支持更復雜的multi-key操作。它們各有所長,但是我們不會推出兩者都支持的API,但我們希望能夠提供實例間數據遷移的命令,并執行multi-key操作。

Redis的作者antirez曾笑稱Redis為一個數據結構服務器(data structures server),我認為這是一個非常準確的表述,Redis的所有功能就是將數據以其固有的幾種結構來保存,并提供給用戶操作這幾種結構的接口。本文將介紹Redis支持的各種數據類型及其操作接口。

strings類型及操作

string是最簡單的類型,你可以理解成與Memcached是一模一樣的類型,一個key對應一個value,其上支持的操作與Memcached的操作類似。但它的功能更豐富。

string類型是二進制安全的。意思是redis的string可以包含任何數據,比如jpg圖片或者序列化的對象。從內部實現來看其實string可以看作byte數組,最大上限是1G字節。

下面是string類型的定義:
struct sdshdr { long len; long free; char buf[]; };
len是buf數組的長度。
free是數組中剩余可用字節數,由此可以理解為什么string類型是二進制安全的了,因為它本質上就是個byte數組,當然可以包含任何數據了
buf是個char數組用于存貯實際的字符串內容,其實char和c#中的byte是等價的,都是一個字節。
另外string類型可以被部分命令按int處理.比如incr等命令,如果只用string類型,redis就可以被看作加上持久化特性的memcached。當然redis對string類型的操作比memcached還是多很多的,具體操作方法如下:


set

設置key對應的值為string類型的value。 例如我們添加一個name= HongWan的鍵值對,可以這樣做:
redis 127.0.0.1:6379> set name HongWan OK redis 127.0.0.1:6379>

setnx

設置key對應的值為string類型的value。如果key已經存在,返回0,nx是not exist的意思。 例如我們添加一個name= HongWan_new的鍵值對,可以這樣做:
redis 127.0.0.1:6379> get name "HongWan" redis 127.0.0.1:6379> setnx name HongWan_new (integer) 0 redis 127.0.0.1:6379> get name "HongWan" redis 127.0.0.1:6379>
由于原來name有一個對應的值,所以本次的修改不生效,且返回碼是0。

setex

設置key對應的值為string類型的value,并指定此鍵值對應的有效期。 例如我們添加一個haircolor= red的鍵值對,并指定它的有效期是10秒,可以這樣做:
redis 127.0.0.1:6379> setex haircolor 10 red OK redis 127.0.0.1:6379> get haircolor "red" redis 127.0.0.1:6379> get haircolor (nil) redis 127.0.0.1:6379>
可見由于最后一次的調用是10秒以后了,所以取不到haicolor這個鍵對應的值。

setrange

設置指定key的value值的子字符串。 例如我們希望將HongWan的126郵箱替換為gmail郵箱,那么我們可以這樣做:
redis 127.0.0.1:6379> get name "HongWan@126.com" redis 127.0.0.1:6379> setrange name 8 gmail.com (integer) 17 redis 127.0.0.1:6379> get name "HongWan@gmail.com" redis 127.0.0.1:6379>
其中的8是指從下標為8(包含8)的字符開始替換

mset

一次設置多個key的值,成功返回ok表示所有的值都設置了,失敗返回0表示沒有任何值被設置。
redis 127.0.0.1:6379> mset key1 HongWan1 key2 HongWan2 OK redis 127.0.0.1:6379> get key1 "HongWan1" redis 127.0.0.1:6379> get key2 "HongWan2" redis 127.0.0.1:6379>

msetnx

一次設置多個key的值,成功返回ok表示所有的值都設置了,失敗返回0表示沒有任何值被設置,但是不會覆蓋已經存在的key。
redis 127.0.0.1:6379> get key1 "HongWan1" redis 127.0.0.1:6379> get key2 "HongWan2" redis 127.0.0.1:6379> msetnx key2 HongWan2_new key3 HongWan3 (integer) 0 redis 127.0.0.1:6379> get key2 "HongWan2" redis 127.0.0.1:6379> get key3 (nil)
可以看出如果這條命令返回0,那么里面操作都會回滾,都不會被執行。

get

獲取key對應的string值,如果key不存在返回nil。 例如我們獲取一個庫中存在的鍵name,可以很快得到它對應的value
redis 127.0.0.1:6379> get name "HongWan" redis 127.0.0.1:6379>
我們獲取一個庫中不存在的鍵name1,那么它會返回一個nil以表時無此鍵值對
redis 127.0.0.1:6379> get name1 (nil) redis 127.0.0.1:6379>

getset

設置key的值,并返回key的舊值。
redis 127.0.0.1:6379> get name "HongWan" redis 127.0.0.1:6379> getset name HongWan_new "HongWan" redis 127.0.0.1:6379> get name "HongWan_new" redis 127.0.0.1:6379>
接下來我們看一下如果key不存的時候會什么樣兒?
redis 127.0.0.1:6379> getset name1 aaa (nil) redis 127.0.0.1:6379>
可見,如果key不存在,那么將返回nil

getrange

獲取指定key的value值的子字符串。 具體樣例如下:
redis 127.0.0.1:6379> get name "HongWan@126.com" redis 127.0.0.1:6379> getrange name 0 6 "HongWan" redis 127.0.0.1:6379>
字符串左面下標是從0開始的
redis 127.0.0.1:6379> getrange name -7 -1 "126.com" redis 127.0.0.1:6379>
字符串右面下標是從-1開始的
redis 127.0.0.1:6379> getrange name 7 100 "@126.com" redis 127.0.0.1:6379>
當下標超出字符串長度時,將默認為是同方向的最大下標

mget

一次獲取多個key的值,如果對應key不存在,則對應返回nil。 具體樣例如下:
`redis 127.0.0.1:6379> mget key1 key2 key3

  1. "HongWan1"
  2. "HongWan2"
  3. (nil)
    redis 127.0.0.1:6379>`
    key3由于沒有這個鍵定義,所以返回nil。
incr

對key的值做加加操作,并返回新的值。注意incr一個不是int的value會返回錯誤,incr一個不存在的key,則設置key為1
redis 127.0.0.1:6379> set age 20 OK redis 127.0.0.1:6379> incr age (integer) 21 redis 127.0.0.1:6379> get age "21" redis 127.0.0.1:6379>

incrby

同incr類似,加指定值 ,key不存在時候會設置key,并認為原來的value是 0
redis 127.0.0.1:6379> get age "21" redis 127.0.0.1:6379> incrby age 5 (integer) 26 redis 127.0.0.1:6379> get name "HongWan@gmail.com" redis 127.0.0.1:6379> get age "26" redis 127.0.0.1:6379>

decr

對key的值做的是減減操作,decr一個不存在key,則設置key為-1
redis 127.0.0.1:6379> get age "26" redis 127.0.0.1:6379> decr age (integer) 25 redis 127.0.0.1:6379> get age "25" redis 127.0.0.1:6379>

decrby

同decr,減指定值。
redis 127.0.0.1:6379> get age "25" redis 127.0.0.1:6379> decrby age 5 (integer) 20 redis 127.0.0.1:6379> get age "20" redis 127.0.0.1:6379>

decrby完全是為了可讀性,我們完全可以通過incrby一個負值來實現同樣效果,反之一樣。
redis 127.0.0.1:6379> get age "20" redis 127.0.0.1:6379> incrby age -5 (integer) 15 redis 127.0.0.1:6379> get age "15" redis 127.0.0.1:6379>

append

給指定key的字符串值追加value,返回新字符串值的長度。 例如我們向name的值追加一個@126.com字符串,那么可以這樣做:
redis 127.0.0.1:6379> append name @126.com (integer) 15 redis 127.0.0.1:6379> get name "HongWan@126.com" redis 127.0.0.1:6379>

strlen

取指定key的value值的長度。
redis 127.0.0.1:6379> get name "HongWan_new" redis 127.0.0.1:6379> strlen name (integer) 11 redis 127.0.0.1:6379> get age "15" redis 127.0.0.1:6379> strlen age (integer) 2 redis 127.0.0.1:6379>

hashes類型及操作

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

hash-max-zipmap-entries 64 #配置字段最多64個
hash-max-zipmap-value 512 #配置value最大為512字節

hset

設置hash field為指定值,如果key不存在,則先創建。
redis 127.0.0.1:6379> hset myhash field1 Hello (integer) 1 redis 127.0.0.1:6379>

hsetnx

設置hash field為指定值,如果key不存在,則先創建。如果field已經存在,返回0,nx是not exist的意思。
redis 127.0.0.1:6379> hsetnx myhash field "Hello" (integer) 1 redis 127.0.0.1:6379> hsetnx myhash field "Hello" (integer) 0 redis 127.0.0.1:6379>
第一次執行是成功的,但第二次執行相同的命令失敗,原因是field已經存在了。

hmset

同時設置hash的多個field。
redis 127.0.0.1:6379> hmset myhash field1 Hello field2 World OK redis 127.0.0.1:6379>

hget

獲取指定的hash field。
redis 127.0.0.1:6379> hget myhash field1 "Hello" redis 127.0.0.1:6379> hget myhash field2 "World" redis 127.0.0.1:6379> hget myhash field3 (nil) redis 127.0.0.1:6379>
由于數據庫沒有field3,所以取到的是一個空值nil

hmget

獲取全部指定的hash filed。
`redis 127.0.0.1:6379> hmget myhash field1 field2 field3

  1. "Hello"
  2. "World"
  3. (nil)
    redis 127.0.0.1:6379>`
    由于數據庫沒有field3,所以取到的是一個空值nil
hincrby

指定的hash filed 加上給定值。
redis 127.0.0.1:6379> hset myhash field3 20 (integer) 1 redis 127.0.0.1:6379> hget myhash field3 "20" redis 127.0.0.1:6379> hincrby myhash field3 -8 (integer) 12 redis 127.0.0.1:6379> hget myhash field3 "12" redis 127.0.0.1:6379>
在本例中我們將field3的值從20降到了12,即做了一個減8的操作。

hexists

測試指定field是否存在。
redis 127.0.0.1:6379> hexists myhash field1 (integer) 1 redis 127.0.0.1:6379> hexists myhash field9 (integer) 0 redis 127.0.0.1:6379>
通過上例可以說明field1存在,但field9是不存在的。

hlen

返回指定hash的field數量。
redis 127.0.0.1:6379> hlen myhash (integer) 4 redis 127.0.0.1:6379>
通過上例可以看到myhash中有4個field。

hdel

返回指定hash的field數量。
redis 127.0.0.1:6379> hlen myhash (integer) 4 redis 127.0.0.1:6379> hdel myhash field1 (integer) 1 redis 127.0.0.1:6379> hlen myhash (integer) 3 redis 127.0.0.1:6379>

hkeys

返回hash的所有field。
`redis 127.0.0.1:6379> hkeys myhash

  1. "field2"
  2. "field"
  3. "field3"
    redis 127.0.0.1:6379>`
    說明這個hash中有3個field
hvals

返回hash的所有value。
`redis 127.0.0.1:6379> hvals myhash

  1. "World"
  2. "Hello"
  3. "12"
    redis 127.0.0.1:6379>`
    說明這個hash中有3個field
hgetall

獲取某個hash中全部的filed及value。
`redis 127.0.0.1:6379> hgetall myhash

  1. "field2"
  2. "World"
  3. "field"
  4. "Hello"
  5. "field3"
  6. "12"
    redis 127.0.0.1:6379>`
    可見,一下子將myhash中所有的field及對應的value都取出來了。

lists類型及操作

list是一個鏈表結構,主要功能是push、pop、獲取一個范圍的所有值等等,操作中key理解為鏈表的名字。

Redis的list類型其實就是一個每個子元素都是string類型的雙向鏈表。鏈表的最大長度是(2的32次方)。我們可以通過push,pop操作從鏈表的頭部或者尾部添加刪除元素。這使得list既可以用作棧,也可以用作隊列。

有意思的是list的pop操作還有阻塞版本的,當我們[lr]pop一個list對象時,如果list是空,或者不存在,會立即返回nil。但是阻塞版本的b[lr]pop則可以阻塞,當然可以加超時時間,超時后也會返回nil。為什么要阻塞版本的pop呢,主要是為了避免輪詢。舉個簡單的例子如果我們用list來實現一個工作隊列。執行任務的thread可以調用阻塞版本的pop去獲取任務這樣就可以避免輪詢去檢查是否有任務存在。當任務來時候工作線程可以立即返回,也可以避免輪詢帶來的延遲。說了這么多,接下來看一下實際操作的方法吧:

lpush

在key對應list的頭部添加字符串元素
`redis 127.0.0.1:6379> lpush mylist "world"
(integer) 1
redis 127.0.0.1:6379> lpush mylist "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist 0 -1

  1. "hello"
  2. "world"
    redis 127.0.0.1:6379>`
    在此處我們先插入了一個world,然后在world的頭部插入了一個hello。其中lrange是用于取mylist的內容。
rpush

在key對應list的尾部添加字符串元素
`redis 127.0.0.1:6379> rpush mylist2 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist2 "world"
(integer) 2
redis 127.0.0.1:6379> lrange mylist2 0 -1

  1. "hello"
  2. "world"
    redis 127.0.0.1:6379>`
    在此處我們先插入了一個hello,然后在hello的尾部插入了一個world。
linsert

在key對應list的特定位置之前或之后添加字符串元素
`redis 127.0.0.1:6379> rpush mylist3 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist3 "world"
(integer) 2
redis 127.0.0.1:6379> linsert mylist3 before "world" "there"
(integer) 3
redis 127.0.0.1:6379> lrange mylist3 0 -1

  1. "hello"
  2. "there"
  3. "world"
    redis 127.0.0.1:6379>`
    在此處我們先插入了一個hello,然后在hello的尾部插入了一個world,然后又在world的前面插入了there。
lset

設置list中指定下標的元素值(下標從0開始)
`redis 127.0.0.1:6379> rpush mylist4 "one"
(integer) 1
redis 127.0.0.1:6379> rpush mylist4 "two"
(integer) 2
redis 127.0.0.1:6379> rpush mylist4 "three"
(integer) 3
redis 127.0.0.1:6379> lset mylist4 0 "four"
OK
redis 127.0.0.1:6379> lset mylist4 -2 "five"
OK
redis 127.0.0.1:6379> lrange mylist4 0 -1

  1. "four"
  2. "five"
  3. "three"
    redis 127.0.0.1:6379>`
    在此處我們依次插入了one,two,three,然后將標是0的值設置為four,再將下標是-2的值設置為five。
lrem

從key對應list中刪除count個和value相同的元素。 count>0時,按從頭到尾的順序刪除,具體如下:
`redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist5 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 4
redis 127.0.0.1:6379> lrem mylist5 2 "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist5 0 -1

  1. "foo"
  2. "hello"
    redis 127.0.0.1:6379>`

count<0時,按從尾到頭的順序刪除,具體如下:

`redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist6 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 4
redis 127.0.0.1:6379> lrem mylist6 -2 "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist6 0 -1

  1. "hello"
  2. "foo"
    redis 127.0.0.1:6379>`

count=0時,刪除全部,具體如下:

`redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist7 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 4
redis 127.0.0.1:6379> lrem mylist7 0 "hello"
(integer) 3
redis 127.0.0.1:6379> lrange mylist7 0 -1

  1. "foo"
    redis 127.0.0.1:6379>`
ltrim

保留指定key 的值范圍內的數據
`redis 127.0.0.1:6379> rpush mylist8 "one"
(integer) 1
redis 127.0.0.1:6379> rpush mylist8 "two"
(integer) 2
redis 127.0.0.1:6379> rpush mylist8 "three"
(integer) 3
redis 127.0.0.1:6379> rpush mylist8 "four"
(integer) 4
redis 127.0.0.1:6379> ltrim mylist8 1 -1
OK
redis 127.0.0.1:6379> lrange mylist8 0 -1

  1. "two"
  2. "three"
  3. "four"
    redis 127.0.0.1:6379>`
lpop

從list的頭部刪除元素,并返回刪除元素
`redis 127.0.0.1:6379> lrange mylist 0 -1

  1. "hello"
  2. "world"
    redis 127.0.0.1:6379> lpop mylist
    "hello"
    redis 127.0.0.1:6379> lrange mylist 0 -1
  3. "world"
    redis 127.0.0.1:6379>`
rpop

從list的尾部刪除元素,并返回刪除元素
`redis 127.0.0.1:6379> lrange mylist2 0 -1

  1. "hello"
  2. "world"
    redis 127.0.0.1:6379> rpop mylist2
    "world"
    redis 127.0.0.1:6379> lrange mylist2 0 -1
  3. "hello"
    redis 127.0.0.1:6379>`
rpoplpush

從第一個list的尾部移除元素并添加到第二個list的頭部,最后返回被移除的元素值,整個操作是原子的.如果第一個list是空或者不存在返回nil
`redis 127.0.0.1:6379> lrange mylist5 0 -1

  1. "three"
  2. "foo"
  3. "hello"
    redis 127.0.0.1:6379> lrange mylist6 0 -1
  4. "hello"
  5. "foo"
    redis 127.0.0.1:6379> rpoplpush mylist5 mylist6
    "hello"
    redis 127.0.0.1:6379> lrange mylist5 0 -1
  6. "three"
  7. "foo"
    redis 127.0.0.1:6379> lrange mylist6 0 -1
  8. "hello"
  9. "hello"
  10. "foo"
    redis 127.0.0.1:6379>`
lindex

返回名稱為key的list中index位置的元素
`redis 127.0.0.1:6379> lrange mylist5 0 -1

  1. "three"
  2. "foo"
    redis 127.0.0.1:6379> lindex mylist5 0
    "three"
    redis 127.0.0.1:6379> lindex mylist5 1
    "foo"
    redis 127.0.0.1:6379>`
llen

返回key對應list的長度
redis 127.0.0.1:6379> llen mylist5 (integer) 2 redis 127.0.0.1:6379>

下一篇: Redis數據類型及操作 Set

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容