安裝
- Docker
docker pull redis
docker run --name myredis -d -p6379:6379 redis
docker exec -it myredis redis-cli
- 直接安裝
brew install redis
# ubuntu
apt-get install redis
# redhat
yum install redis
# 運行客戶端
redis-cli
- 源碼啟動
windows版:
https://github.com/MSOpenTech/redis/releases
解壓:Redis-x64-3.2.100.zip
在解壓目錄下
啟動服務(wù)
redis-server.exe
啟動客戶端,連接服務(wù)器
redis-cli.exe
安裝為windows服務(wù)
redis-server.exe --service-install redis.windows.conf
卸載服務(wù):
redis-server --service-uninstall
開啟服務(wù):
redis-server --service-start
停止服務(wù):
redis-server --service-stop
重命名服務(wù):
redis-server --service-name name
應(yīng)用場景
- 記錄帖子點贊數(shù),評論數(shù),點擊數(shù)
- 緩存用戶行為歷史
- 有關(guān)id
數(shù)據(jù)結(jié)構(gòu)
五種數(shù)據(jù)結(jié)構(gòu)
- string
- list
- set
- hash
- zset
1. 字符串string
string 是redis最簡單的數(shù)據(jù)結(jié)構(gòu)界睁,所有數(shù)據(jù)結(jié)構(gòu)的key都是以唯一的string作為名稱,然后通過key去找到value。不同類型的數(shù)據(jù)結(jié)構(gòu)的差異都是value的結(jié)構(gòu)不一樣。
字符串的一個常見的用處就是緩存用戶信息官觅,將用戶信息結(jié)構(gòu)體使用json序列化成字符串,然后將序列化的字符串存到redis中阐污。同樣取出用戶信息休涤,也要進行反序列化。
redis的字符串是動態(tài)字符串笛辟,可以修改的字符串功氨,內(nèi)部結(jié)構(gòu)類似與ArrayList,采用預(yù)分配空間來減少內(nèi)存的頻分分配手幢,內(nèi)部當(dāng)前的字符串空間要大于實際字符串長度捷凄。當(dāng)字符串小于1m的時候,擴容都是加倍現(xiàn)有的空間围来,如果超過1m跺涤,每次擴用只會擴1m。字符串的最大長度是512m
設(shè)置
#設(shè)置鍵值對
set key1 value1
#根據(jù)鍵獲取值
get key1
#判斷是否存在
exists key1
#刪除
del key1
批量操作
#設(shè)置
mset key1 value1 key2 value2
#獲取
mget key1 key2
對key設(shè)置過期時間
#key1 5秒過期
expire key1 5
#expire 等價 setex
setex key2 5 value2
#如果 name 不存在就執(zhí)行 set 創(chuàng)建
setnx key1 value1
計數(shù)
set age 30
#自增
incr age
#減少6
incrby age -6
#自增是有范圍的 在signed long之間
字符串有多個字節(jié)組成管钳,每個字節(jié)由8個bit組成钦铁,可將字符串看成多個bit組成,這邊是bitmap數(shù)據(jù)結(jié)構(gòu)[位圖]
list
list相當(dāng)于linkedList才漆,它是鏈表結(jié)構(gòu)牛曹,插入和刪除快,索引定位慢
當(dāng)彈出最后一個元素醇滥,該數(shù)據(jù)結(jié)構(gòu)被刪除黎比,內(nèi)存自動回收。
list常用來做異步隊列使用
- 隊列 左進右出
rpush key value1 value2
llen key
lpop key #value1
lpop key # value2
- 椩妫 右進右出
rpush key value1 value2
rpop key #value2
rpop key #value1
- 慢操作
相當(dāng)于get(index)阅虫,需要對鏈表進行遍歷,隨著index的增大性能變慢不跟。ltrim跟著兩個參數(shù)(start_index,end_index)定義了一個區(qū)間颓帝,區(qū)間內(nèi)的保留,區(qū)間外的全部砍掉窝革」撼牵可以通過ltrim實現(xiàn)一個定長的鏈表。index可以為負(fù)數(shù)虐译,-1是倒數(shù)第一個元素瘪板,-2是倒數(shù)第二個元素
rpush books python java golang
lindex books 1 # O(n) 慎用
lrange books 0 -1 # 獲取所有元素,O(n) 慎用
ltrim books 1 -1 # O(n) 慎用
lrange books 0 -1
ltrim books 1 0 # 這其實是清空了整個列表漆诽,因為區(qū)間范圍長度為負(fù)
- 快速列表
redis底層存儲不是簡單的LinkedList 侮攀,而是一個快速列表quicklist的一個結(jié)構(gòu)
首先锣枝,在列表元素較少的情況下,會使用一塊連續(xù)的內(nèi)存存儲兰英,稱之為ziplist撇叁,即壓縮列表。將所有的元素緊挨著存儲箭昵,分配的是一塊連續(xù)的內(nèi)存税朴。當(dāng)數(shù)據(jù)量大的時候會使用quicklist。因為普通的鏈表需要附件的指針空間太大家制,比較浪費空間,加重內(nèi)存的碎片化泡一。
比如一個列表中只存int類型的數(shù)據(jù)颤殴,但是需要prev和next兩個額外的指針,所以將列表和ziplist結(jié)合起來組成了quiklist鼻忠。就是將多個ziplist使用雙向指針穿起來使用涵但。滿足了插入和刪除快,又不會出現(xiàn)太大的空間 冗余
字典hash
相當(dāng)于HashMap帖蔓。是無序字典矮瘟。內(nèi)部結(jié)構(gòu)和HashMap一致。是數(shù)組+鏈表的二維結(jié)構(gòu)塑娇。第一維數(shù)組的hash碰撞的時候澈侠,就會將碰撞的hash用鏈表存儲起來。
但是redis的字典只能是string 埋酬。另外和HashMap的rehash不一樣哨啃,reahs是一個很耗時的操作,HashMap在的字典很大的時候写妥,需要一次全部rehash拳球。而redis為可高性能,不能堵塞服務(wù)珍特,采用了漸進式rehash
漸進式rehash祝峻,會在rehash的同時,保留新舊兩個hash扎筒,查詢時會查詢兩個hash結(jié)構(gòu)莱找,然后在后續(xù)的定時任務(wù)以及hash的子指令中,將舊hash的內(nèi)容遷移到新hash中砸琅。當(dāng)舊hash移除最后一個元素宋距,該數(shù)據(jù)結(jié)構(gòu)被刪除,內(nèi)存被回收症脂。
hash結(jié)構(gòu)也可以存用戶信息谚赎,不同于字符串一次性需要全部 序列化整個對象淫僻,hash可以對用戶結(jié)構(gòu)的每個字段進行單獨存儲,當(dāng)需要獲取用戶信息可以進行部分獲取壶唤,而以字符串的形式進行存儲用戶信息只能一次性全部讀取完雳灵,比較浪費網(wǎng)絡(luò)流量。
hash也有缺點闸盔,hash結(jié)構(gòu)的存儲消耗要高于單個字符串悯辙,使用hash還是字符串根據(jù)實際情況來定。
> hset books java "think in java" # 命令行的字符串如果包含空格迎吵,要用引號括起來
(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" # 因為是更新操作,所以返回 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)中的單個子 key 也可以進行計數(shù)拢蛋,它對應(yīng)的指令是 hincrby,
和 incr 使用基本一樣蔫巩。
> hincrby user-laoqian age 1
(integer) 30
Set集合
相當(dāng)于HashSet谆棱,內(nèi)部的鍵值對是無序唯一的。內(nèi)部實現(xiàn)相當(dāng)于一個特殊的字典圆仔。字典中所有的value都有一個值NULL
當(dāng)集合中最后一個元素移除之后垃瞧,數(shù)據(jù)結(jié)構(gòu)自動刪除,內(nèi)存被回收坪郭。 set 結(jié)構(gòu)可以用來存儲活動中獎的用戶 ID个从,因為有去重功能,可以保證同一個用戶不會中獎兩次截粗。
> sadd books python
(integer) 1
> sadd bookspython # 重復(fù)
(integer) 0
> sadd books java golang
(integer) 2
> smembers books # 注意順序信姓,和插入的并不一致,因為 set 是無序的
1) "java"
2) "python"
3) "golang"
> sismember books java # 查詢某個 value 是否存在绸罗,相當(dāng)于 contains(o)
(integer) 1
> sismember books rust
(integer) 0
> scard books # 獲取長度相當(dāng)于 count()
(integer) 3
> spop books # 彈出一個
"java"
zset (有序列表)
類似與HashMap和SortSet的結(jié)合意推。它是一個set,保證了內(nèi)部value的唯一性珊蟀,又給每個value賦予了score菊值,代表了value的排序權(quán)重。內(nèi)部實現(xiàn)是[跳躍列表]的數(shù)據(jù)結(jié)構(gòu)育灸。
zset 中最后一個 value 被移除后腻窒,數(shù)據(jù)結(jié)構(gòu)自動刪除,內(nèi)存被回收磅崭。 zset 可以用來存粉絲列表儿子,value 值是粉絲的用戶 ID,score 是關(guān)注時間砸喻。我們可以對粉絲列表按關(guān)注時間進行排序柔逼。
zset 還可以用來存儲學(xué)生的成績蒋譬,value 值是學(xué)生的 ID,score 是他的考試成績愉适。我們可以對成績按分?jǐn)?shù)進行排序就可以得到他的名次犯助。
> 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.9000000000000004" # 內(nèi)部 score 使用 double 類型進行存儲剂买,所以存在小數(shù)點精度問題
> 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,同時返
回分值癌蓖。inf 代表 infinite瞬哼,無窮大的意思。
1) "java cookbook"
2) "8.5999999999999996"
3) "java concurrency"
4) "8.9000000000000004"
> zrem books "java concurrency" # 刪除 value
(integer) 1
> zrange books 0 -1
1) "java cookbook"
2) "think in java"
跳躍列表
因為zset支持隨機插入和刪除费坊,所以不好使用數(shù)組表示倒槐,采用跳躍列表。
跳躍列表中的元素可以身兼數(shù)職附井,如上圖中間的元素,同時處于L0两残,L1永毅,L2中,可以快速在不同層級進行跳躍人弓。
定位插入點時沼死,先在頂層進行定位,然后下潛到下一級定位崔赌,一直下潛到最底層找到合適的位置意蛀,將新元素插進去。你也許會問健芭,那新插入的元素如何才有機會「身兼數(shù)職」呢县钥?
跳躍列表采取一個隨機策略來決定新元素可以兼職到第幾層。
首先 L0 層肯定是 100% 了慈迈,L1 層只有 50% 的概率若贮,L2 層只有 25% 的概率,L3 層只有 12.5% 的概率痒留,一直隨機到最頂層 L31 層谴麦。絕大多數(shù)元素都過不了幾層,只有極少數(shù)元素可以深入到頂層伸头。列表中的元素越多匾效,能夠深入的層次就越深,能進入到頂層的概率就會越大恤磷。