基礎(chǔ) : 萬丈高樓平地起 ——Redis 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

Redis 安裝
體驗(yàn) Redis 需要使用 Linux 或者 Mac 環(huán)境巩掺,如果是 Windows 可以考慮使用虛擬機(jī)醇锚。
主要方式有四種:
1、使用 Docker 安裝题山。
2、通過 Github 源碼編譯故痊。
3顶瞳、直接安裝 apt-get install(Ubuntu)、yum install(RedHat) 或者 brew install(Mac)愕秫。
4慨菱、如果讀者懶于安裝操作,也可以使用網(wǎng)頁(yè)版的 Web Redis 直接體驗(yàn)戴甩。
具體操作如下:
Docker 方式

拉取 redis 鏡像

docker pull redis

運(yùn)行 redis 容器

docker run --name myredis -d -p6379:6379 redis

執(zhí)行容器中的 redis-cli符喝,可以直接使用命令行操作 redis

docker exec -it myredis redis-cli
Github 源碼編譯方式

下載源碼

git clone --branch 2.8 --depth 1 git@github.com:antirez/redis.git
cd redis

編譯

make
cd src

運(yùn)行服務(wù)器,daemonize 表示在后臺(tái)運(yùn)行

./redis-server --daemonize yes

運(yùn)行命令行

./redis-cli
直接安裝方式

mac

brew install redis

ubuntu

apt-get install redis

redhat

yum install redis

運(yùn)行客戶端

redis-cli

Redis 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

Redis 有 5 種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)甜孤,分別為:string (字符串)协饲、list (列表)、set (集合)缴川、hash (哈
希) 和 zset (有序集合)茉稠。

string ( 字符串)

字符串 string 是 Redis 最簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)。Redis 所有的數(shù)據(jù)結(jié)構(gòu)都是以唯一的 key
字符串作為名稱把夸,然后通過這個(gè)唯一 key 值來獲取相應(yīng)的 value 數(shù)據(jù)而线。不同類型的數(shù)據(jù)結(jié)
構(gòu)的差異就在于 value 的結(jié)構(gòu)不一樣。
字符串結(jié)構(gòu)使用非常廣泛,一個(gè)常見的用途就是緩存用戶信息膀篮。我們將用戶信息結(jié)構(gòu)體
使用 JSON 序列化成字符串嘹狞,然后將序列化后的字符串塞進(jìn) Redis 來緩存。同樣誓竿,取用戶
信息會(huì)經(jīng)過一次反序列化的過程磅网。
Redis 的字符串實(shí)際分配的空間 一般要高于實(shí)際字符串長(zhǎng)度 len。當(dāng)字符串長(zhǎng)度小于 1M 時(shí)烤黍,
擴(kuò)容都是加倍現(xiàn)有的空間知市,如果超過 1M,擴(kuò)容時(shí)一次只會(huì)多擴(kuò) 1M 的空間速蕊。需要注意的是
字符串最大長(zhǎng)度為 512M嫂丙。

鍵值對(duì)

set name codehole
OK
get name
"codehole"
exists name
(integer) 1
del name
(integer) 1
get name
(nil)
批量鍵值對(duì)
可以批量對(duì)多個(gè)字符串進(jìn)行讀寫,節(jié)省網(wǎng)絡(luò)耗時(shí)開銷规哲。
set name1 codehole
OK
set name2 holycoder
OK
mget name1 name2 name3 # 返回一個(gè)列表

  1. "codehole"
  2. "holycoder"
  3. (nil)

mset name1 boy name2 girl name3 unknown
mget name1 name2 name3

  1. "boy"
  2. "girl"
  3. "unknown"

過期和 set 命令擴(kuò)展

可以對(duì) key 設(shè)置過期時(shí)間跟啤,到點(diǎn)自動(dòng)刪除,這個(gè)功能常用來控制緩存的失效時(shí)間唉锌。

set name codehole
get name "codehole"
expire name 5 # 5s 后過期
... # wait for 5s
get name
(nil)
setex name 5 codehole # 5s 后過期隅肥,等價(jià)于 set+expire
get name
"codehole"
... # wait for 5s
get name
(nil)
setnx name codehole # 如果 name 不存在就執(zhí)行 set 創(chuàng)建
(integer) 1
get name
"codehole"
setnx name holycoder
(integer) 0 # 因?yàn)?name 已經(jīng)存在,所以 set 創(chuàng)建不成功
get name
"codehole"# 沒有改變

計(jì)數(shù)

如果 value 值是一個(gè)整數(shù)袄简,還可以對(duì)它進(jìn)行自增操作腥放。自增是有范圍的,它的范圍是
signed long 的最大最小值绿语,超過了這個(gè)值秃症,Redis 會(huì)報(bào)錯(cuò)。

set age 30
OK
incr age
(integer) 31
incrby age 5
(integer) 36
incrby age -5
(integer) 31
set codehole 9223372036854775807# Long.Max
OK
incr codehole
(error) ERR increment or decrement would overflow

list ( 列表)

Redis 的列表相當(dāng)于 Java 語(yǔ)言里面的 LinkedList吕粹,注意它是鏈表而不是數(shù)組种柑。這意味著
list 的插入和刪除操作非常快匹耕,時(shí)間復(fù)雜度為 O(1)聚请,但是索引定位很慢,時(shí)間復(fù)雜度為
O(n)稳其。
當(dāng)列表彈出了最后一個(gè)元素之后驶赏,該數(shù)據(jù)結(jié)構(gòu)自動(dòng)被刪除,內(nèi)存被回收欢际。
Redis 的列表結(jié)構(gòu)常用來做異步隊(duì)列使用母市。將需要延后處理的任務(wù)結(jié)構(gòu)體序列化成字符
串塞進(jìn) Redis 的列表,另一個(gè)線程從這個(gè)列表中輪詢數(shù)據(jù)進(jìn)行處理损趋。

右邊進(jìn)左邊出:隊(duì)列

rpush books python java golang
(integer) 3
llen books
(integer) 3
lpop books
"python"
lpop books
"java"
lpop books
"golang"
lpop books
(nil)

右邊進(jìn)右邊出:棧

rpush books python java golang
(integer) 3
rpop books
"golang"
rpop books
"java"
rpop books
"python"
rpop books
(nil)

慢操作

lindex 相當(dāng)于 Java 鏈表的 get(int index)方法患久,它需要對(duì)鏈表進(jìn)行遍歷椅寺,性能隨著參數(shù)
index 增大而變差。 ltrim 和字面上的含義不太一樣蒋失,個(gè)人覺得它叫 lretain(保留) 更合適一
些返帕,因?yàn)?ltrim 跟的兩個(gè)參數(shù) start_index 和 end_index 定義了一個(gè)區(qū)間,在這個(gè)區(qū)間內(nèi)的值篙挽,
ltrim 要保留荆萤,區(qū)間之外統(tǒng)統(tǒng)砍掉。我們可以通過 ltrim 來實(shí)現(xiàn)一個(gè)定長(zhǎng)的鏈表铣卡,這一點(diǎn)非常
有用链韭。index 可以為負(fù)數(shù),index=-1 表示倒數(shù)第一個(gè)元素煮落,同樣 index=-2 表示倒數(shù)第二個(gè)元
素敞峭。

rpush books python java golang
(integer) 3
lindex books 1 # O(n) 慎用
"java"
lrange books 0 -1 # 獲取所有元素,O(n) 慎用

  1. "python"
  2. "java"
  3. "golang"

ltrim books 1 -1 # O(n) 慎用
OK
lrange books 0 -1

  1. "java"
  2. "golang"

ltrim books 1 0 # 這其實(shí)是清空了整個(gè)列表蝉仇,因?yàn)閰^(qū)間范圍長(zhǎng)度為負(fù)
OK
llen books
(integer) 0

hash ( 字典)

Redis 的字典相當(dāng)于 Java 語(yǔ)言里面的 HashMap旋讹,它是無序字典。內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)上同
Java 的 HashMap 也是一致的轿衔,同樣的數(shù)組 + 鏈表二維結(jié)構(gòu)沉迹。
不同的是,Redis 的字典的值只能是字符串害驹,另外它們 rehash 的方式不一樣鞭呕,因?yàn)?br> Java 的 HashMap 在字典很大時(shí),rehash 是個(gè)耗時(shí)的操作宛官,需要一次性全部 rehash琅拌。Redis
為了高性能,不能堵塞服務(wù)摘刑,所以采用了漸進(jìn)式 rehash 策略。

rehash :簡(jiǎn)單說 rehash 就是因?yàn)?HashMap 的空間不夠用了刻坊,所以需要分配一個(gè)大一點(diǎn)的空間枷恕,然后保存在里面的內(nèi)容需要重新計(jì)算 hash。隨著操作的不斷執(zhí)行谭胚, 哈希表保存的鍵值對(duì)會(huì)逐漸地增多或者減少徐块, 為了讓哈希表的負(fù)載因子(load factor)維持在一個(gè)合理的范圍之內(nèi), 當(dāng)哈希表保存的鍵值對(duì)數(shù)量太多或者太少時(shí)灾而, 程序需要對(duì)哈希表的大小進(jìn)行相應(yīng)的擴(kuò)展或者收縮胡控。

漸進(jìn)式 rehash 會(huì)在 rehash 的同時(shí),保留新舊兩個(gè) hash 結(jié)構(gòu)旁趟,查詢時(shí)會(huì)同時(shí)查詢兩個(gè)
hash 結(jié)構(gòu)昼激,然后在后續(xù)的定時(shí)任務(wù)中以及 hash 的子指令中,循序漸進(jìn)地將舊 hash 的內(nèi)容
一點(diǎn)點(diǎn)遷移到新的 hash 結(jié)構(gòu)中。
當(dāng) hash 移除了最后一個(gè)元素之后橙困,該數(shù)據(jù)結(jié)構(gòu)自動(dòng)被刪除瞧掺,內(nèi)存被回收。
hash 結(jié)構(gòu)也可以用來存儲(chǔ)用戶信息凡傅,不同于字符串一次性需要全部序列化整個(gè)對(duì)象辟狈,
hash 可以對(duì)用戶結(jié)構(gòu)中的每個(gè)字段單獨(dú)存儲(chǔ)。這樣當(dāng)我們需要獲取用戶信息時(shí)可以進(jìn)行部分
獲取夏跷。而以整個(gè)字符串的形式去保存用戶信息的話就只能一次性全部讀取哼转,這樣就會(huì)比較浪
費(fèi)網(wǎng)絡(luò)流量。
hash 也有缺點(diǎn)槽华,hash 結(jié)構(gòu)的存儲(chǔ)消耗要高于單個(gè)字符串壹蔓,到底該使用 hash 還是字符
串,需要根據(jù)實(shí)際情況再三權(quán)衡硼莽。

hset books java "think in java" # 命令行的字符串如果包含空格庶溶,要用引號(hào)括起來
(integer) 1
hset books golang "concurrency in go"
(integer) 1
hset books python "python cookbook"
(integer) 1
hgetall books # entries(),key 和 value 間隔出現(xiàn)

  1. "java"
  2. "think in java"
  3. "golang"
  4. "concurrency in go"
  5. "python"
  6. "python cookbook"
    hlen books
    (integer) 3
    hget books java
    "think in java"
    hset books golang "learning go programming" # 因?yàn)槭歉虏僮鞫遥苑祷?0
    (integer) 0
    hget books golang
    "learning go programming"
    hmset books java "effective java" python "learning python" golang "modern golang
    programming" # 批量 set
    OK

同字符串一樣偏螺,hash 結(jié)構(gòu)中的單個(gè)子 key 也可以進(jìn)行計(jì)數(shù),它對(duì)應(yīng)的指令是 hincrby匆光,
和 incr 使用基本一樣套像。

hincrby user-laoqian age 1
(integer) 1

set ( 集合)

Redis 的集合相當(dāng)于 Java 語(yǔ)言里面的 HashSet,它內(nèi)部的鍵值對(duì)是無序的唯一的终息。它的
內(nèi)部實(shí)現(xiàn)相當(dāng)于一個(gè)特殊的字典夺巩,字典中所有的 value 都是一個(gè)值 NULL。
當(dāng)集合中最后一個(gè)元素移除之后周崭,數(shù)據(jù)結(jié)構(gòu)自動(dòng)刪除柳譬,內(nèi)存被回收。 set 結(jié)構(gòu)可以用來
存儲(chǔ)活動(dòng)中獎(jiǎng)的用戶 ID续镇,因?yàn)橛腥ブ毓δ苊腊模梢员WC同一個(gè)用戶不會(huì)中獎(jiǎng)兩次。

sadd books python
(integer) 1
sadd books python # 重復(fù)
(integer) 0
sadd books java golang
(integer) 2
smembers books # 注意順序摸航,和插入的并不一致制跟,因?yàn)?set 是無序的

  1. "java"
  2. "python"
  3. "golang"
    sismember books java # 查詢某個(gè) value 是否存在,相當(dāng)于 contains(o)
    (integer) 1
    sismember books rust
    (integer) 0
    scard books # 獲取長(zhǎng)度相當(dāng)于 count()
    (integer) 3
    spop books # 彈出一個(gè)
    "java"

zset ( 有序列表) )

zset 可能是 Redis 提供的最為特色的數(shù)據(jù)結(jié)構(gòu)酱虎。它類似于 Java 的 SortedSet 和 HashMap 的結(jié)合體雨膨,一方面它是一個(gè) set,保證了內(nèi)部
value 的唯一性读串,另一方面它可以給每個(gè) value 賦予一個(gè) score聊记,代表這個(gè) value 的排序權(quán)
重撒妈。它的內(nèi)部實(shí)現(xiàn)用的是一種叫著「跳躍列表」的數(shù)據(jù)結(jié)構(gòu)。
zset 中最后一個(gè) value 被移除后甥雕,數(shù)據(jù)結(jié)構(gòu)自動(dòng)刪除踩身,內(nèi)存被回收。 zset 可以用來存
粉絲列表社露,value 值是粉絲的用戶 ID挟阻,score 是關(guān)注時(shí)間。我們可以對(duì)粉絲列表按關(guān)注時(shí)間
進(jìn)行排序峭弟。
zset 還可以用來存儲(chǔ)學(xué)生的成績(jī)附鸽,value 值是學(xué)生的 ID,score 是他的考試成績(jī)瞒瘸。我們
可以對(duì)成績(jī)按分?jǐn)?shù)進(jìn)行排序就可以得到他的名次坷备。

zadd books 9.0 "think in java"
(integer) 1
zadd books 8.9 "java concurrency"
(integer) 1
zadd books 8.6 "java cookbook"
(integer) 1
zrange books 0 -1 # 按 score 排序列出,參數(shù)區(qū)間為排名范圍

  1. "java cookbook"
  2. "java concurrency"
  3. "think in java"

zrevrange books 0 -1 # 按 score 逆序列出情臭,參數(shù)區(qū)間為排名范圍

  1. "think in java"
  2. "java concurrency"
  3. "java cookbook"

zcard books # 相當(dāng)于 count()
(integer) 3
zscore books "java concurrency" # 獲取指定 value 的 score
"8.9"
zrank books "java concurrency" # 排名
(integer) 1
zrangebyscore books 0 8.91 # 根據(jù)分值區(qū)間遍歷 zset

  1. "java cookbook"
  2. "java concurrency"
    zrangebyscore books -inf 8.91 withscores # 根據(jù)分值區(qū)間 (-∞, 8.91] 遍歷 zset省撑,同時(shí)返
    回分值。inf 代表 infinite俯在,無窮大的意思竟秫。
  3. "java cookbook"
  4. "8.6"
  5. "java concurrency"
  6. "8.9"
    zrem books "java concurrency" # 刪除 value
    (integer) 1
    zrange books 0 -1
  7. "java cookbook"
  8. "think in java"

容器型數(shù)據(jù)結(jié)構(gòu)的通用規(guī)則

list/set/hash/zset 這四種數(shù)據(jù)結(jié)構(gòu)是容器型數(shù)據(jù)結(jié)構(gòu),它們共享下面兩條通用規(guī)則:
1 跷乐、create if not exists
如果容器不存在肥败,那就創(chuàng)建一個(gè),再進(jìn)行操作愕提。比如 rpush 操作剛開始是沒有列表的馒稍,
Redis 就會(huì)自動(dòng)創(chuàng)建一個(gè),然后再 rpush 進(jìn)去新元素浅侨。
2 纽谒、drop if no elements
如果容器里元素沒有了,那么立即刪除元素如输,釋放內(nèi)存佛舱。這意味著 lpop 操作到最后一
個(gè)元素,列表就消失了挨决。

過期時(shí)間

Redis 所有的數(shù)據(jù)結(jié)構(gòu)都可以設(shè)置過期時(shí)間,時(shí)間到了订歪,Redis 會(huì)自動(dòng)刪除相應(yīng)的對(duì)象脖祈。
需要注意的是過期是以對(duì)象為單位,比如一個(gè) hash 結(jié)構(gòu)的過期是整個(gè) hash 對(duì)象的過期刷晋,
而不是其中的某個(gè)子 key盖高。
還有一個(gè)需要特別注意的地方是如果一個(gè)字符串已經(jīng)設(shè)置了過期時(shí)間慎陵,然后你調(diào)用了
set 方法修改了它,它的過期時(shí)間會(huì)消失喻奥。

127.0.0.1:6379> set codehole yoyo
OK
127.0.0.1:6379> expire codehole 600
(integer) 1
127.0.0.1:6379> ttl codehole
(integer) 597
127.0.0.1:6379> set codehole yoyo
OK
127.0.0.1:6379> ttl codehole
(integer) -1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末席纽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子撞蚕,更是在濱河造成了極大的恐慌润梯,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甥厦,死亡現(xiàn)場(chǎng)離奇詭異纺铭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)刀疙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門舶赔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谦秧,你說我怎么就攤上這事竟纳。” “怎么了疚鲤?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵锥累,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我石咬,道長(zhǎng)揩悄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任鬼悠,我火速辦了婚禮删性,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘焕窝。我一直安慰自己蹬挺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布它掂。 她就那樣靜靜地躺著巴帮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虐秋。 梳的紋絲不亂的頭發(fā)上榕茧,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音客给,去河邊找鬼用押。 笑死,一個(gè)胖子當(dāng)著我的面吹牛靶剑,可吹牛的內(nèi)容都是我干的蜻拨。 我是一名探鬼主播池充,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缎讼!你這毒婦竟也來了收夸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤血崭,失蹤者是張志新(化名)和其女友劉穎卧惜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體功氨,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡序苏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捷凄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忱详。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖跺涤,靈堂內(nèi)的尸體忽然破棺而出匈睁,到底是詐尸還是另有隱情,我是刑警寧澤桶错,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布航唆,位于F島的核電站,受9級(jí)特大地震影響院刁,放射性物質(zhì)發(fā)生泄漏糯钙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一退腥、第九天 我趴在偏房一處隱蔽的房頂上張望任岸。 院中可真熱鬧,春花似錦狡刘、人聲如沸享潜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)剑按。三九已至,卻和暖如春澜术,著一層夾襖步出監(jiān)牢的瞬間艺蝴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工鸟废, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猜敢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锣枝,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子兰英,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容