1. 概述
Redis 一個開源的基于鍵值對(Key-Value)NoSQL 數(shù)據(jù)庫社搅。使用 ANSIC 語言編寫、支持網(wǎng)絡(luò)乳规、基于內(nèi)存但支持持久化形葬。性能優(yōu)秀,并提供多種語言的 API驯妄。
我們要首先理解一點(diǎn)荷并,我們把 Redis 稱為 KV 數(shù)據(jù)庫,鍵值對數(shù)據(jù)庫青扔,那就可以把 Redis 內(nèi)部的存儲視為存在著一個巨大的 Map源织,對 Map 的操作無非就是get 和 put,然后通過 key 操作這個 key 所對應(yīng)的 value微猖,而這個 value 的類型可以多種多樣谈息,也就是 Redis 為我們提供的那些數(shù)據(jù)結(jié)構(gòu),比如字符串(String)凛剥、哈希(Hash)等等侠仇。
Redis 會將所有數(shù)據(jù)都存放在內(nèi)存中,所以它的讀寫性能非常驚人犁珠。不僅如此逻炊,Redis 還可以將內(nèi)存的數(shù)據(jù)利用快照和日志的形式保存到硬盤上,這樣在發(fā)生類似斷電或者機(jī)器故障的時候犁享,內(nèi)存中的數(shù)據(jù)不會丟失余素。
除了上述功能以外,Redis 還提供了鍵過期炊昆、發(fā)布訂閱桨吊、事務(wù)威根、流水線、Lua 腳本等附加功能视乐。
1.1 應(yīng)用場景
緩存
緩存機(jī)制幾乎在所有的大型網(wǎng)站都有使用洛搀,合理地使用緩存不僅可以加快數(shù)據(jù)的訪問速度,而且能夠有效地降低后端數(shù)據(jù)源的壓力佑淀。Redis 提供了鍵值過期時間設(shè)置留美,并且也提供了靈活控制最大內(nèi)存和內(nèi)存溢出后的淘汰策略≡郏可以這么說独榴,一個合理的緩存設(shè)計(jì)能夠?yàn)橐粋€網(wǎng)站的穩(wěn)定保駕護(hù)航。
排行榜系統(tǒng)
排行榜系統(tǒng)幾乎存在于所有的網(wǎng)站奕枝,例如按照熱度排名的排行榜,按照發(fā)布時間的排行榜瓶堕,按照各種復(fù)雜維度計(jì)算出的排行榜隘道,Redis 提供了列表和有序集合數(shù)據(jù)結(jié)構(gòu),合理地使用這些數(shù)據(jù)結(jié)構(gòu)可以很方便地構(gòu)建各種排行榜系統(tǒng)郎笆。
計(jì)數(shù)器應(yīng)用
計(jì)數(shù)器在網(wǎng)站中的作用至關(guān)重要谭梗,例如視頻網(wǎng)站有播放數(shù)、電商網(wǎng)站有瀏覽數(shù)宛蚓,為了保證數(shù)據(jù)的實(shí)時性激捏,每一次播放和瀏覽都要做+1
的操作,如果并發(fā)量很大對于傳統(tǒng)關(guān)系型數(shù)據(jù)的性能是一種挑戰(zhàn)凄吏。Redis 天然支持計(jì)數(shù)功能而且計(jì)數(shù)的性能也非常好远舅,可以說是計(jì)數(shù)器系統(tǒng)的重要選擇。
社交網(wǎng)絡(luò)
贊/踩痕钢、粉絲图柏、共同好友/喜好、推送任连、下拉刷新等是社交網(wǎng)站的必備功能蚤吹,由于社交網(wǎng)站訪問量通常比較大,而且傳統(tǒng)的關(guān)系型數(shù)據(jù)不太適合保存這種類型的數(shù)據(jù)随抠,Redis 提供的數(shù)據(jù)結(jié)構(gòu)可以相對比較容易地實(shí)現(xiàn)這些功能裁着。
消息隊(duì)列系統(tǒng)
消息隊(duì)列系統(tǒng)可以說是一個大型網(wǎng)站的必備基礎(chǔ)組件,因?yàn)槠渚哂袠I(yè)務(wù)解耦拱她、 非實(shí)時業(yè)務(wù)削峰等特性二驰。Redis 提供了發(fā)布訂閱功能和阻塞隊(duì)列的功能,雖然和專業(yè)的消息隊(duì)列比還不夠足夠強(qiáng)大椭懊,但是對于一般的消息隊(duì)列功能基本可以滿足诸蚕。(我上家公司就用過 Redis 做消息隊(duì)列步势,雖然后面換了其他 MQ)。
1.2 特性
速度快
正常情況下背犯,Redis 執(zhí)行命令的速度非郴荡瘢快,官方給出的數(shù)字是讀寫性能可以達(dá)到 10 萬/秒漠魏。
基于鍵值對的數(shù)據(jù)結(jié)構(gòu)服務(wù)器
幾乎所有的編程語言都提供了類似字典的功能倔矾,例如 Java 里的 map,類似于這種組織數(shù)據(jù)的方式叫作基于鍵值的方式柱锹,與很多鍵值對數(shù)據(jù)庫不同的是哪自,Redis 中的值不僅可以是字符串,而且還可以是具體的數(shù)據(jù)結(jié)構(gòu)禁熏,這樣不僅能便于在許多應(yīng)用場景的開發(fā)壤巷,同時也能夠提高開發(fā)效率。
Redis 的全稱是 Remote Dictionary Server瞧毙,它主要提供了 5 種數(shù)據(jù)結(jié)構(gòu):字符串胧华、哈希、列表宙彪、集合矩动、有序集合,同時在字符串的基礎(chǔ)之上演變出了位圖(Bitmaps)和 HyperLogLog 兩種數(shù)據(jù)結(jié)構(gòu)释漆,并且隨著 LBS (Location BasedService悲没,基于位置服務(wù))的不斷發(fā)展,Redis 中加入有關(guān) GEO(地理信息定位)的功能男图。
豐富的功能
除了 5 種數(shù)據(jù)結(jié)構(gòu)示姿,Redis 還提供了許多額外的功能:提供了鍵過期功能,可以用來實(shí)現(xiàn)緩存享言。
提供了發(fā)布訂閱功能峻凫,可以用來實(shí)現(xiàn)消息系統(tǒng)。支持 Lua 腳本功能览露,可以利用 Lua 創(chuàng)造出新的 Redis 命令荧琼。提供了簡單的事務(wù)功能,能在一定程度上保證事務(wù)特性差牛。提供了流水線(Pipeline)功能命锄,這樣客戶端能將一批命令一次性傳到 Redis,減少了網(wǎng)絡(luò)的開銷偏化。
簡單穩(wěn)定
Redis 的簡單主要表現(xiàn)在三個方面脐恩。
首先,Redis 的源碼很少侦讨,早期版本的代碼只有 2 萬行左右驶冒,3.0 版本以后由于添加了集群特性苟翻,代碼增至 5 萬行左右。
其次骗污,Redis 使用單線程模型崇猫,這樣不僅使得 Redis 服務(wù)端處理模型變得簡單,而且也使得客戶端開發(fā)變得簡單需忿。
最后诅炉,Redis 不需要依賴于操作系統(tǒng)中的類庫。
Redis 雖然很簡單屋厘,但是不代表它不穩(wěn)定涕烧。實(shí)際的運(yùn)行中很少出現(xiàn)因?yàn)?Redis 自身 bug 而宕掉的情況。
客戶端語言多
Redis 提供了簡單的 TCP 通信協(xié)議汗洒,很多編程語言可以很方便地接人到 Redis议纯。
持久化
通常看仲翎,將數(shù)據(jù)放在內(nèi)存中是不安全的痹扇,一旦發(fā)生斷電或者機(jī)器故障,重要的數(shù)據(jù)可能就會丟失溯香,因此Redis提供了兩種持久化方式:RDB 和 AOF,即可以用兩種策略將內(nèi)存的數(shù)據(jù)保存到硬盤中浓恶,這樣就保證了數(shù)據(jù)的可持久性玫坛。
主從復(fù)制
Redis 提供了復(fù)制功能,實(shí)現(xiàn)了多個相同數(shù)據(jù)的 Redis 副本,復(fù)制功能是分布式Redis 的基礎(chǔ)包晰。
高可用和分布式
Redis Sentinel湿镀,它能夠保證 Redis 節(jié)點(diǎn)的故障發(fā)現(xiàn)和故障自動轉(zhuǎn)移。Redis 從 3.0 版本正式提供了分布式實(shí)現(xiàn) Redis Cluster伐憾,它是 Redis 真正的分布式實(shí)現(xiàn)勉痴,提供了高可用、讀寫和容量的擴(kuò)展性树肃。
2. 下載安裝
目前演示的安裝操作是基于 Centos7 下講解蒸矛。
官方下載地址:https://redis.io/download
# 先新建一個目錄
mkdir /usr/local/redis
cd /usr/local/redis
# 下載
wget https://download.redis.io/releases/redis-6.2.6.tar.gz
# 解壓
tar -xzvf redis-6.2.6.tar.gz
# 編譯
cd redis-6.2.6
make
2.1 啟動
Redis 有三種方法啟動 Redis:默認(rèn)配置、帶參數(shù)啟動胸嘴、配置文件啟動雏掠。
1?? 默認(rèn)配置
進(jìn)入安裝好的 Redis 的 src
目錄下執(zhí)行以下命令:
./redis-server
可以看到直接使用 redis-server 啟動 Redis 后,會打印出一些日志劣像,通過日志 可以看到一些信息:
當(dāng)前的 Redis 版本的是 64 位的 6.2.6乡话,默認(rèn)端口是 6379。Redis 建議使用配置文件來啟動耳奕,所以這種方式是不會在生產(chǎn)環(huán)境中使用的绑青。
2?? 參數(shù)啟動
redis-server 加上要修改配置名和值(可以是多對)诬像,沒有設(shè)置的配置將使用默認(rèn)配置,例如:如果要用 6380 作為端口啟動 Redis闸婴,那么可以執(zhí)行:
./redis-server --port 6380
不過這種方式一般也用得比較少坏挠。
3?? 配置文件啟動
將配置寫到指定文件里,并啟動掠拳,主要修改的是安裝目錄下的 redis.conf
文件癞揉。
./redis-server ../redis.conf
2.2 操作
Redis 服務(wù)啟動完成后,就可以使用 redis-cli 連接和操作 Redis 服務(wù)溺欧。
2.3 停止
Redis 提供了 shutdown 命令來停止 Redis 服務(wù)喊熟,例如我們目前已經(jīng)啟動的 Redis 服務(wù),可以執(zhí)行:
./redis-cli -p 6379 shutdown
Redis 服務(wù)端將會顯示:
2853:M 15 Dec 2021 20:41:26.593 # User requested shutdown...
2853:M 15 Dec 2021 20:41:26.593 * Saving the final RDB snapshot before exiting.
2853:M 15 Dec 2021 20:41:26.594 * DB saved on disk
2853:M 15 Dec 2021 20:41:26.594 * Removing the pid file.
2853:M 15 Dec 2021 20:41:26.594 # Redis is now ready to exit, bye bye...
除了可以通過 shutdown 命令關(guān)閉 Redis 服務(wù)以外姐刁,還可以通過 kill 進(jìn)程號的方式關(guān)閉掉 Redis芥牌,但是強(qiáng)烈不建議使用 kill -9
強(qiáng)制殺死 Redis 服務(wù),不但不會做持久化操作聂使,還會造成緩沖區(qū)等資源不能被優(yōu)雅關(guān)閉壁拉,極端情況會造成 AOF 和復(fù)制丟失數(shù)據(jù)的情況。
shutdown 還有一個參數(shù)柏靶,代表是否在關(guān)閉 Redis 前弃理,生成持久化文件:
./redis-cli -p 6379 shutdown nosave/save
默認(rèn)是 save,生成持久化文件屎蜓,如果是 nosave 則不生成持久化文件痘昌。
3. 全局命令
在了解 Redis 的數(shù)據(jù)結(jié)構(gòu)之前,先了解 Redis 的一些全局命令炬转。
命令 | 說明 |
---|---|
keys * | 查看所有鍵辆苔,同時也支持通配符,如 keys n* |
dbsize | 返回當(dāng)前數(shù)據(jù)庫中鍵的總數(shù) |
exists | 檢查鍵是否存在扼劈,存在返回 1驻啤,不存在返回 0,如 exists name |
del | 刪除鍵荐吵,無論值是什么數(shù)據(jù)結(jié)構(gòu)類型骑冗,del 命令都可以將其刪除。返回刪除鍵個數(shù)捍靠,刪除不存在鍵返回 0沐旨。同時 del 命令可以支持刪除多個鍵,如 del name age |
expire | Redis 支持對鍵添加過期時間榨婆,當(dāng)超過過期時間后磁携,會自動刪除鍵,時間單位秒良风,如 expire name 10 |
ttl | ttl 命令會返回鍵的剩余過期時間谊迄,若返回 -1 則表示鍵沒設(shè)置過期時間闷供,-2 鍵不存在 |
type | 返回鍵的數(shù)據(jù)結(jié)構(gòu)類型 |
randomkey | 隨機(jī)返回一個鍵 |
rename | 鍵重命名,為了防止被強(qiáng)行 rename统诺,Redis 提供了 renamenx 命令歪脏,確保只有 newKey 不存在時候才被覆蓋。由于重命名鍵期間會執(zhí)行 del 命令刪除舊的鍵粮呢,如果鍵對應(yīng)的值比較大婿失,會存在阻塞 Redis 的可能性 |
注:
- dbsize 命令在計(jì)算鍵總數(shù)時不會遍歷所有鍵,而是直接獲取 Redis 內(nèi)置的鍵總數(shù)變量,所以 dbsize 命令的時間復(fù)雜度是 O(1)。而 keys 命令會遍歷所有鍵啄寡,所以它的時間復(fù)雜度是 o(n)豪硅,當(dāng) Redis 保存了大量鍵時線上環(huán)境禁止使用 keys 命令;
- 除了 expire挺物、ttl 命令以外懒浮,Redis 還提供了 expireat、pexpire,pexpireat识藤、pttl砚著、persist 等一系列命令,可自行查驗(yàn)痴昧。
4. 基本數(shù)據(jù)結(jié)構(gòu)
Redis 提供了一些數(shù)據(jù)結(jié)構(gòu)供我們往 Redis 中存取數(shù)據(jù)稽穆,最常用的的有 5 種,字符串(String)赶撰、哈希(Hash)秧骑、列表(list)、集合(set)扣囊、有序集合(ZSET)。
4.1 String
字符串類型是 Redis 最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)绒疗。首先鍵都是字符串類型侵歇,而且其他幾種數(shù)據(jù)結(jié)構(gòu)都是在字符串類型基礎(chǔ)上構(gòu)建的,所以字符串類型能為其他四種數(shù)據(jù)結(jié)構(gòu)的學(xué)習(xí)奠定基礎(chǔ)吓蘑。字符串類型的值實(shí)際可以是字符串(簡單的字符串惕虑、復(fù) 雜的字符串(例如 JSON、XML))磨镶、數(shù)字(整數(shù)溃蔫、浮點(diǎn)數(shù)),甚至是二進(jìn)制(圖片琳猫、音頻伟叛、視頻),但是值最大不能超過 512 MB脐嫂。
1?? 常用命令
設(shè)置值 set
set key value [ex seconds] [px milliseconds] [nxlxx]
-
ex seconds
:為鍵設(shè)置秒級過期時間统刮。 -
px milliseconds
:為鍵設(shè)置毫秒級過期時間紊遵。 -
nx
:鍵必須不存在,才可以設(shè)置成功侥蒙,用于添加暗膜。 -
xx
:與 nx 相反,鍵必須存在鞭衩,才可以設(shè)置成功学搜,用于更新。
其中论衍,ex 參數(shù)和 expire 命令基本一樣瑞佩。還有一個需要特別注意的地方是如果一個字符串已經(jīng)設(shè)置了過期時間,然后你調(diào)用了 set 方法修改了它饲齐,它的過期時間會消失钉凌。
除了 set 選項(xiàng),Redis 還提供了 setex 和 setnx 兩個命令:
setex key seconds value
setnx key value
setex 和 setnx 的作用和 ex 和 nx 選項(xiàng)是一樣的捂人。也就是御雕,setex 為鍵設(shè)置秒級過期時間,setnx 設(shè)置時鍵必須不存在滥搭,才可以設(shè)置成功酸纲。
有什么應(yīng)用場景嗎?
以 setnx 命令為例子,由于 Redis 的單線程命令處理機(jī)制瑟匆,如果有多個客戶端同時執(zhí)行 setnx key value闽坡,根據(jù) setnx 的特性只有一個客戶端能設(shè)置成功,setnx 可以作為分布式鎖的一種實(shí)現(xiàn)方案愁溜。
獲取值 get
get key
如果要獲取的鍵不存在疾嗅,則返回 nil
。
另外冕象,除了單個設(shè)置和獲取鍵值代承,Redis 還支持批量操作。
批量設(shè)置值 mset
mset name ayue age 20 sex 男
批量獲取值 mget
mget name age sex
如果有些鍵不存在渐扮,那么它的值為 nil
论悴,結(jié)果是按照傳入鍵的順序返回。
批量操作命令可以有效提高效率墓律,假如沒有 mget 這樣的命令膀估,要執(zhí)行 n 次 get 命令具體耗時如下:
n 次 get 時間 = n 次網(wǎng)絡(luò)時間 + n 次命令時間
使用 mget 命令后,要執(zhí)行 n 次 get 命令操作具體耗時如下:
n 次 get 時間 = 1 次網(wǎng)絡(luò)時間 + n 次命令時間
Redis 可以支撐每秒數(shù)萬的讀寫操作耻讽,但是這指的是 Redis 服務(wù)端的處理能力察纯,對于客戶端來說,一次命令除了命令時間還是有網(wǎng)絡(luò)時間,假設(shè)網(wǎng)絡(luò)時間為 1 毫秒捐寥,命令時間為 0.1 毫秒(按照每秒處理 1 萬條命令算)笤昨,那么執(zhí)行 1000 次 get 命令需要 1.1 秒(1000*1+1000*0.1=1100ms
),1 次 mget 命令的需要 0.101 秒 (1*1+1000*0.1=101ms
)握恳。
數(shù)字運(yùn)算 incr
incr 命令用于對值做自增操作瞒窒,返回結(jié)果分為三種情況:
- 值不是整數(shù),返回錯誤乡洼;
- 值是整數(shù)崇裁,返回自增后的結(jié)果;
- 鍵不存在束昵,按照值為 0 自增,返回結(jié)果為 1拔稳。
incr key
除了 incr 命令,Redis 提供了 decr(自減)锹雏、 incrby(自增指定數(shù)字)巴比、decrby(自減指定數(shù)字)、incrbyfloat(自增浮點(diǎn)數(shù))礁遵。
追加指令 append
append 可以向字符串尾部追加值轻绞。
append key value
strlen
返回字符串長度。
strlen key
截取字符串 getrange
getrange 截取字符串中的一部分佣耐,形成一個子串政勃,需要指明開始和結(jié)束的偏移量,截取的范圍是個閉區(qū)間兼砖。
命令 | 說明 | 時間復(fù)雜度 |
---|---|---|
get key | 獲取值 | O(1) |
del key [key ...] | 刪除key | O(N)(N是鍵的個數(shù)) |
mset key [key value ...] | 批量設(shè)置值 | O(N)(N是鍵的個數(shù)) |
mget key [key ...] | 批量獲取值 | O(N)(N是鍵的個數(shù)) |
incr key | 將 key 中儲存的數(shù)字值增一 | O(1) |
decr key | 將 key 中儲存的數(shù)字值減一 | O(1) |
incrby key increment | 將 key 所儲存的值加上給定的增量值(increment) | O(1) |
decrby key increment | key 所儲存的值減去給定的減量值(decrement) | O(1) |
incrbyfloat key increment | 將 key 所儲存的值加上給定的浮點(diǎn)增量值(increment) | O(1) |
append key value | 如果 key 已經(jīng)存在并且是一個字符串奸远, APPEND 命令將指定的 value 追加到該 key 原來值(value)的末尾 | O(1) |
strlen key | 返回 key 所儲存的字符串值的長度。 | O(1) |
setrange key offset value | 用 value 參數(shù)覆寫給定 key 所儲存的字符串值讽挟,從偏移量 offset 開始 | O(1) |
getrange key start end | 返回 key 中字符串值的子字符 | O(N)(N是字符串的長度) |
2?? 命令的時間復(fù)雜度
字符串這些命令中懒叛,除了 del 、mset耽梅、 mget 支持多個鍵的批量操作芍瑞,時間復(fù)雜度和鍵的個數(shù)相關(guān),為 O(n)褐墅,getrange 和字符串長度相關(guān),也是 O(n)洪己,其余的命令基本上都是 O(1)的時間復(fù)雜度妥凳,在速度上是非常快的答捕。
3?? 使用場景
字符串類型的使用場景很廣泛逝钥,如下:
1、緩存功能
Redis 作為緩存層,MySQL 作為存儲層艘款,絕大部分請求的數(shù)據(jù)都是從 Redis 中獲取持际。由于 Redis 具有支撐高并發(fā)的特性,所以緩存通常能起到加速讀寫和降低 后端壓力的作用哗咆。
2蜘欲、計(jì)數(shù)
使用 Redis 作為計(jì)數(shù)的基礎(chǔ)工具,它可以實(shí)現(xiàn)快速計(jì)數(shù)晌柬、查詢緩存的功能姥份,同時數(shù)據(jù)可以異步落地到其他數(shù)據(jù)源。
3年碘、共享 Session
一個分布式 Web 服務(wù)將用戶的 Session 信息(例如用戶登錄信息)保存在各 自服務(wù)器中澈歉,這樣會造成一個問題,出于負(fù)載均衡的考慮屿衅,分布式服務(wù)會將用戶的訪問均衡到不同服務(wù)器上埃难,用戶刷新一次訪問可能會發(fā)現(xiàn)需要重新登錄,這個問題是用戶無法容忍的涤久。
為了解決這個問題涡尘, 可以使用 Redis 將用戶的 Session 進(jìn)行集中管理,在這種模式下只要保證 Redis 是高可用和擴(kuò)展性的拴竹,每次用戶更新或者查詢登錄信息都直接從 Redis 中集中獲取悟衩。
4、限時
很多應(yīng)用出于安全的考慮栓拜,會在每次進(jìn)行登錄時座泳,讓用戶輸入手機(jī)驗(yàn)證碼,從而確定是否是用戶本人幕与。但是為了短信接口不被頻繁訪問挑势,會限制用戶每分鐘獲取驗(yàn)證碼的頻率,例如一分鐘不能超過 5 次啦鸣。一些網(wǎng)站限制一個 IP 地址不能在一秒鐘之內(nèi)訪問超過 n 次潮饱。或者同一 IP 在短時間內(nèi)多次瀏覽謀篇文章瀏覽次數(shù)不會一直增加诫给。點(diǎn)贊次數(shù)在短時間內(nèi)不能重復(fù)點(diǎn)贊香拉。
4.2 Hash
Redis hash 是一個 string 類型的 field(字段) 和 value(值) 的映射表,hash 特別適合用于存儲對象中狂。
Redis 中每個 hash 可以存儲 232 - 1 鍵值對(40多億)凫碌。
1?? 常用命令
基本上,哈希的操作命令和字符串的操作命令很類似胃榕,很多命令在字符串類型的命令前面加上了 h 字母盛险,代表是操作哈希類型,同時還要指明要操作的 field 的值籽懦。
hset
hset key field value
如果設(shè)置成功會返回 1纬傲,反之會返回 0。此外 Redis 提供了 hsetnx 命令涯塔,它們的關(guān)系就像 set 和 setnx 命令一樣鹤啡,只不過作用域由鍵變?yōu)?field惯驼。
127.0.0.1:6379> hset hash:test name ayue
(integer) 1
127.0.0.1:6379>
hget
hget key field
獲取值
127.0.0.1:6379> hget hash:test name
"ayue"
127.0.0.1:6379>
其他命令:
命令 | 說明 | 時間復(fù)雜度 |
---|---|---|
HDEL key field [field] | 刪除一個或多個Hash的field | O(N) N是被刪除的字段數(shù)量 |
HEXISTS key field | 判斷field是否存在于Hash中 | O(1) |
HGET key field | 獲取Hash中field的值 | O(1) |
HGETALL key | 從Hash中讀取全部的域和值 | O(N) N是Hash的長度 |
HINCRBY key field increment | 將Hash中指定域的值增加給定的數(shù)字 | O(1) |
HINCRBYFLOAT key field increment | 將Hash中指定域的值增加給定的浮點(diǎn)數(shù) | O(1) |
HKEYS key | 獲取Hash的所有字段 | O(N) N是Hash的長度 |
HLEN key | 獲取Hash里所有字段的數(shù)量 | O(1) |
HMGET key field field | 獲取Hash里面指定字段的值 | O(N) N是請求的字段數(shù) |
HMSET key field value [field value ...] | 批量設(shè)置Hash字段值 | O(N) N是設(shè)置的字段數(shù) |
HSET key field value | 設(shè)置Hash里面一個字段的值 | O(1) |
HSETNX key field value | 設(shè)置Hash的一個字段,只有當(dāng)這個字段不存在時有效 | O(1) |
HSTRLEN key field | 獲取Hash里面指定field的長度 | O(1) |
HVALS key | 獲得 Hash 的所有值 | O(N) N是Hash的長度 |
HSCAN key cursor [MATCH pattern] [COUNT count] | 迭代 Hash 里面的元素 |
2?? 命令的時間復(fù)雜度
哈希類型的操作命令中揉忘,hdel跳座,hmget,hmset 的時間復(fù)雜度和命令所帶的 field 的個數(shù)相關(guān) O(k)泣矛,hkeys疲眷,hgetall,hvals 和存儲的 field 的總數(shù)相關(guān)您朽,O(N)狂丝。其余的命令時間復(fù)雜度都是 O(1)。
3?? 使用場景
1哗总、存儲對象
Redis哈希對象常常用來緩存一些對象信息几颜,如用戶信息、商品信息讯屈、配置信息等蛋哭。
我們以用戶信息為例,它在關(guān)系型數(shù)據(jù)庫中的結(jié)構(gòu)是這樣的:
id | name | age |
---|---|---|
1 | Tom | 15 |
2 | Jerry | 13 |
而使用Redis Hash存儲其結(jié)構(gòu)如下圖:
hmset user:1 name Tom age 15
hmset user:2 name Jerry age 13
相比較于使用Redis字符串存儲涮母,其有以下幾個優(yōu)缺點(diǎn):
-
原生字符串每個屬性一個鍵谆趾。
set user:1:name Tom set user:1:age 15
優(yōu)點(diǎn):簡單直觀,每個屬性都支持更新操作叛本。
缺點(diǎn):占用過多的鍵沪蓬,內(nèi)存占用量較大,同時用戶信息內(nèi)聚性比較差来候,所以此種方案一般不會在生產(chǎn)環(huán)境使用跷叉。 -
序列化字符串后,將用戶信息序列化后用一個鍵保存
set user:1 serialize(userInfo)
優(yōu)點(diǎn):簡化編程营搅,如果合理的使用序列化可以提高內(nèi)存的使用效率云挟。
缺點(diǎn):序列化和反序列化有一定的開銷,同時每次更新屬性都需要把全部數(shù)據(jù)取出進(jìn)行反序列化转质,更新后再序列化到Redis中植锉。 -
序列化字符串后,將用戶信息序列化后用一個鍵保存
hmset user:1 name Tom age 15
優(yōu)點(diǎn):簡單直觀峭拘,如果使用合理可以減少內(nèi)存空間的使用。
缺點(diǎn):要控制哈希在ziplist和hashtable兩種內(nèi)部編碼的轉(zhuǎn)換,hashtable會消耗更多內(nèi)存鸡挠。
2辉饱、購物車
購物車主要功能是臨時存放欲購買的商品,然后在結(jié)算或下訂單時拣展,把購物里面的數(shù)據(jù)全部移除彭沼。其數(shù)據(jù)結(jié)構(gòu)主要包含的字段有:用戶ID、商品ID备埃、商品數(shù)量等等姓惑。通常我們需要實(shí)現(xiàn)以下幾個功能:
- 全選功能,獲取所有該用戶的所有購物車商品按脚;
- 商品數(shù)量于毙,購物車圖標(biāo)上要顯示購物車?yán)锷唐返目倲?shù);
- 刪除辅搬,要能移除購物車?yán)锬硞€商品唯沮;
- 增加或減少某個商品的數(shù)量。
在之前很多電商網(wǎng)站通過 cookie 實(shí)現(xiàn)購物車功能堪遂,也就是將整個購物車都存儲到 cookie里面介蛉。
- 優(yōu)點(diǎn):無須對數(shù)據(jù)庫進(jìn)行寫入就可以實(shí)現(xiàn)購物車功能,這種方式大大提高了購物車的性能。
- 缺點(diǎn):程序需要重新解析和驗(yàn)證( validate) cookie溶褪,確保 cookie 的格式正確币旧,并且包含的商品都是真正可購買的商品。另外猿妈,因?yàn)闉g覽器每次發(fā)送請求都會連 cookie 一起發(fā)送吹菱,所以如果購物車 cookie 的體積比較大,那么請求發(fā)送和處理的速度可能會有所降低于游。
而通過 Redis 定義購物車非常簡單:當(dāng)前登錄用戶 ID 號做為key毁葱,商品 ID 號為 field,加入購物車數(shù)量為 value贰剥,如下:
hmset cart:001 prod:01 1 prod:02 1
| | |
| | |
| | |
key field value
而對于上述功能倾剿,可以通過 Hash 的相關(guān)命令來操作。
4.3 List
列表( list)類型是用來存儲多個有序的字符串蚌成,a前痘、b、c担忧、d芹缔、e 五個元素從左到右組成了一個有序的列表,列表中的每個字符串稱為元素(element)瓶盛,一個列表最多可以存儲 2-1 個元素最欠。
在 Redis 中示罗,可以對列表兩端插入( push)和彈出(pop),還可以獲取指定范圍的元素列表芝硬、獲取指定索引下標(biāo)的元素等蚜点。
列表是一種比較靈活的數(shù)據(jù)結(jié)構(gòu),它可以充當(dāng)棧和隊(duì)列的角色拌阴,在實(shí)際開發(fā)上有很多應(yīng)用場景绍绘。
列表類型有兩個特點(diǎn):
- 列表中的元素是有序的,這就意味著可以通過索引下標(biāo)獲取某個元素或者某個范圍內(nèi)的元素列表迟赃。
- 列表中的元素可以是重復(fù)的陪拘。
1?? 常用命令
Redis列表對象常用命令如下表(點(diǎn)擊命令可查看命令詳細(xì)說明):
命令 | 說明 | 時間復(fù)雜度 |
---|---|---|
BLPOP key [key ...] timeout | 刪除,并獲得該列表中的第一元素纤壁,或阻塞左刽,直到有一個可用 | O(1) |
BRPOP key [key ...] timeout | 刪除,并獲得該列表中的最后一個元素摄乒,或阻塞悠反,直到有一個可用 | O(1) |
BRPOPLPUSH source destination timeout | 彈出一個列表的值,將它推到另一個列表馍佑,并返回它;或阻塞斋否,直到有一個可用 | O(1) |
LINDEX key index | 獲取一個元素,通過其索引列表 | O(N) |
LINSERT key BEFORE | AFTER pivot value在列表中的另一個元素之前或之后插入一個元素 | O(N) |
LLEN key | 獲得隊(duì)列(List)的長度 | O(1) |
LPOP key | 從隊(duì)列的左邊出隊(duì)一個元素 | O(1) |
LPUSH key value [value ...] | 從隊(duì)列的左邊入隊(duì)一個或多個元素 | O(1) |
LPUSHX key value | 當(dāng)隊(duì)列存在時拭荤,從隊(duì)到左邊入隊(duì)一個元素 | O(1) |
LRANGE key start stop | 從列表中獲取指定返回的元素 | O(S+N) |
LREM key count value | 從列表中刪除元素 | O(N) |
LSET key index value | 設(shè)置隊(duì)列里面一個元素的值 | O(N) |
LTRIM key start stop | 修剪到指定范圍內(nèi)的清單 | O(N) |
RPOP key | 從隊(duì)列的右邊出隊(duì)一個元 | O(1) |
RPOPLPUSH source destination | 刪除列表中的最后一個元素茵臭,將其追加到另一個列表 | O(1) |
RPUSH key value [value ...] | 從隊(duì)列的右邊入隊(duì)一個元素 | O(1) |
RPUSHX key value | 從隊(duì)列的右邊入隊(duì)一個元素,僅隊(duì)列存在時有效 | O(1) |
2?? 命令的時間復(fù)雜度
列表類型的操作命令中舅世,llen旦委,lpop,rpop雏亚,blpop 和 brpop 命令時間復(fù)雜度都是 O(1)缨硝,其余的命令的時間復(fù)雜度都是 O(n),只不過 n 的值根據(jù)命令不同而不同罢低,比如 lset查辩,lindex 時間復(fù)雜度和命令后的索引值大小相關(guān),rpush 和 lpush 和插入元素的個數(shù)相關(guān)等等网持。
3?? 使用場景
1宜岛、消息隊(duì)列
但使用 Redis 做消息隊(duì)列存在很多問題,如消息確認(rèn) ACK功舀,消息丟失等萍倡,所以一般來說還是用比較專業(yè)的 MQ 中間件。
2辟汰、文章列表
如下面這樣的文章列表列敲,當(dāng)用戶和文章都越來越多時阱佛,為了加快程序的響應(yīng)速度,我們可以把用戶自己的文章存入到 List 中戴而,因?yàn)?List 是有序的結(jié)構(gòu)瘫絮,所以這樣又可以完美的實(shí)現(xiàn)分頁功能,從而加速了程序的響應(yīng)速度填硕。
上圖可表示為:
# 深圳衛(wèi)健委發(fā)布一條消息,消息ID為 99
lpush mes:001 99
# 武漢本地寶發(fā)布一條消息鹿鳖,消息ID為 100
lpush mes:001 100
# 獲取消息列表‘
lrange mes:001 0 5
4.4 Set
集合( set)類型也是用來保存多個的字符串元素,但和列表類型不一樣的是扁眯,集合中不允許有重復(fù)元素,并且集合中的元素是無序的翅帜,不能通過索引下標(biāo)獲取元素姻檀。
一個集合最多可以存儲 232 - 1 個元素。Redis 除了支持集合內(nèi)的增刪改查涝滴,同時還支持多個集合取交集绣版、并集、差集歼疮,合理地使用好集合類型杂抽,能在實(shí)際開發(fā)中解決很多實(shí)際問題。
1?? 常用命令
Redis Set 對象常用命令如下表(點(diǎn)擊命令可查看命令詳細(xì)說明):
命令 | 說明 | 時間復(fù)雜度 |
---|---|---|
SADD key member [member ...] | 添加一個或者多個元素到集合(set)里 | O(N) |
SCARD key | 獲取集合里面的元素數(shù)量 | O(1) |
SDIFF key [key ...] | 獲得隊(duì)列不存在的元素 | O(N) |
SDIFFSTORE destination key [key ...] | 獲得隊(duì)列不存在的元素韩脏,并存儲在一個關(guān)鍵的結(jié)果集 | O(N) |
SINTER key [key ...] | 獲得兩個集合的交集 | O(N*M) |
SINTERSTORE destination key [key ...] | 獲得兩個集合的交集缩麸,并存儲在一個關(guān)鍵的結(jié)果集 | O(N*M) |
SISMEMBER key member | 確定一個給定的值是一個集合的成員 | O(1) |
SMEMBERS key | 獲取集合里面的所有元素 | O(N) |
SMOVE source destination member | 移動集合里面的一個元素到另一個集合 | O(1) |
SPOP key [count] | 刪除并獲取一個集合里面的元素 | O(1) |
SRANDMEMBER key [count] | 從集合里面隨機(jī)獲取一個元素 | |
SREM key member [member ...] | 從集合里刪除一個或多個元素 | O(N) |
SUNION key [key ...] | 添加多個set元素 | O(N) |
SUNIONSTORE destination key [key ...] | 合并set元素,并將結(jié)果存入新的set里面 | O(N) |
[SSCAN key cursor MATCH pattern] [COUNT count] | 迭代set里面的元素 | O(1) |
2?? 命令的時間復(fù)雜度
scard赡矢,sismember 時間復(fù)雜度為 O(1)杭朱,其余的命令時間復(fù)雜度為 O(n),其中 sadd吹散,srem 和命令后所帶的元素個數(shù)相關(guān)弧械,spop,srandmember 和命令后所帶 count 值相關(guān)空民,交集運(yùn)算 O(m*k)刃唐,k 是多個集合中元素最少的個數(shù),m 是鍵個數(shù)袭景,并集唁桩、差集和所有集合的元素個數(shù)和相關(guān)。
3?? 使用場景
1耸棒、抽獎活動
常見的抽獎活動荒澡,比如基于 Redis 實(shí)現(xiàn)抽獎功能。
SPOP(隨機(jī)移除并返回集合中一個或多個元素) 和 SRANDMEMBER(隨機(jī)返回集合中一個或多個元素) 命令可以幫助我們實(shí)現(xiàn)一個抽獎系統(tǒng)与殃,如果允許重復(fù)中獎单山,可以使用SRANDMEMBER 命令碍现。
活動 ID 為 001,則
# Tom userID:01 參加活動
sadd action:001 01
# Jerry userID:02 參加活動
sadd action:001 02
# 開始抽獎1名中獎?wù)?srandmember action:001 1 或 spop action:001 1
# 查看有多少用戶參加了本次抽獎
smembers action:001
2米奸、點(diǎn)贊功能
比如設(shè)計(jì)一個微信點(diǎn)贊功能昼接。
# 張三用戶ID 為userId:01
# 張三對消息 ID008點(diǎn)贊啦
sadd zan:008 userId:01
# 張三取消了對消息008的點(diǎn)贊
srem zan:008 userId:01
# 檢查用戶是否點(diǎn)過贊
sismember zan:008 userId:01
# 獲取消息ID008所有的點(diǎn)贊用戶列表
smembers zan:008
# 消息ID008的點(diǎn)贊數(shù)計(jì)算
scard zan:008
3、關(guān)系設(shè)計(jì)
如我們要設(shè)計(jì)一個微博的共同關(guān)注悴晰,或者可能認(rèn)識的人慢睡。設(shè)計(jì)如下:
① A 關(guān)注的人
sadd A:cares B C D E
② B 關(guān)注的人
sadd B:cares A C D F
③ C 關(guān)注的人
sadd C:cares A F
按照以上條件:
④ A 和 B 共同關(guān)注的人
# D,C
sinter A:cares B:cares
⑤ 我關(guān)注的人也關(guān)注他
# A 關(guān)注的 B 也關(guān)注了 F,返回 1 否則返回 0
sismember B:cares F
⑥ 可能認(rèn)識的人
# C 可能認(rèn)識的人 C,D
sdiff B:cares C:cares
4铡溪、集合操作
setA={A,B,C} setB={B, C}
① 集合與集合之間的交集
sinter setA setB-->得到集合{B,C}
② 集合與集合之間的并集
sunion setA setB -->得到集合{A,B,C}
③ 集合與集合之間的差集
sdiff setA setB-->得到集合{A}
127.0.0.1:6379> SADD setA A B C
(integer) 3
127.0.0.1:6379> SADD setB B C
(integer) 2
127.0.0.1:6379> SINTER setA setB
1) "C"
2) "B"
127.0.0.1:6379> SUNION setA setB
1) "A"
2) "B"
3) "C"
127.0.0.1:6379> SDIFF setA setB
1) "A"
127.0.0.1:6379>
4.5 ZSet
ZSet漂辐,有序集合,相對于哈希棕硫、列表髓涯、集合來說會有一點(diǎn)點(diǎn)陌生,但既然叫有序集合哈扮,那么它和集合必然有著聯(lián)系纬纪,它保留了集合不能有重復(fù)成員的特性,但不同的是滑肉,有序集合中的元素可以排序包各。但是它和列表使用索引下標(biāo)作為排序依據(jù)不同的是,它給每個元素設(shè)置一個分?jǐn)?shù)( score)作為排序的依據(jù)赦邻。
有序集合中的元素不能重復(fù)髓棋,但是 score 可以重復(fù),就和一個班里的同學(xué)學(xué)號不能重復(fù)惶洲,但是考試成績可以相同按声。
有序集合提供了獲取指定分?jǐn)?shù)和元素范圍查詢、計(jì)算成員排名等功能恬吕,合理的利用有序集合签则,能幫助我們在實(shí)際開發(fā)中解決很多問題。
1?? 常用命令
zadd
向有序集合 top:20211221
添加話題和點(diǎn)擊量铐料。
zadd hot:20211220 10 薇婭逃稅
zadd 命令還有四個選項(xiàng) nx渐裂、xx、ch钠惩、incr 四個選項(xiàng):
- nx柒凉,member 必須不存在,才可以設(shè)置成功篓跛,用于添加膝捞;
- xx,member 必須存在愧沟,才可以設(shè)置成功蔬咬,用于更新鲤遥;
- ch,返回此次操作后林艘,有序集合元素和分?jǐn)?shù)發(fā)生變化的個數(shù)盖奈;
- incr,對 score 做增加狐援,相當(dāng)于 zincrby 钢坦。
Redis列表對象常用命令如下表:
命令 | 說明 | 時間復(fù)雜度 |
---|---|---|
BZPOPMAX key [key ...] timeout | 從一個或多個排序集中刪除并返回得分最高的成員,或阻塞啥酱,直到其中一個可用為止 | O(log(N)) |
BZPOPMIN key [key ...] timeout | 從一個或多個排序集中刪除并返回得分最低的成員场钉,或阻塞,直到其中一個可用為止 | O(log(N)) |
[ZADD key NXXX] [CH] [INCR] score member [score member ...] | 添加到有序set的一個或多個成員懈涛,或更新的分?jǐn)?shù),如果它已經(jīng)存在 | O(log(N)) |
ZCARD key | 獲取一個排序的集合中的成員數(shù)量 | O(1) |
ZCOUNT key min max | 返回分?jǐn)?shù)范圍內(nèi)的成員數(shù)量 | O(log(N)) |
ZINCRBY key increment member | 增量的一名成員在排序設(shè)置的評分 | O(log(N)) |
ZINTERSTORE | 相交多個排序集泳猬,導(dǎo)致排序的設(shè)置存儲在一個新的關(guān)鍵 | O(NK)+O(Mlog(M)) |
ZLEXCOUNT key min max | 返回成員之間的成員數(shù)量 | O(log(N)) |
ZPOPMAX key [count] | 刪除并返回排序集中得分最高的成員 | O(log(N)*M) |
ZPOPMIN key [count] | 刪除并返回排序集中得分最低的成員 | O(log(N)*M) |
ZRANGE key start stop [WITHSCORES] | 根據(jù)指定的index返回批钠,返回sorted set的成員列表 | O(log(N)+M) |
ZRANGEBYLEX key min max [LIMIT offset count] | 返回指定成員區(qū)間內(nèi)的成員,按字典正序排列, 分?jǐn)?shù)必須相同得封。 | O(log(N)+M) |
ZREVRANGEBYLEX key max min [LIMIT offset count] | 返回指定成員區(qū)間內(nèi)的成員埋心,按字典倒序排列,分?jǐn)?shù)必須相同 | O(log(N)+M) |
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] | 返回有序集合中指定分?jǐn)?shù)區(qū)間內(nèi)的成員忙上,分?jǐn)?shù)由低到高排序拷呆。 | O(log(N)+M) |
ZRANK key member | 確定在排序集合成員的索引 | O(log(N)) |
ZREM key member [member ...] | 從排序的集合中刪除一個或多個成員 | O(M*log(N)) |
ZREMRANGEBYLEX key min max | 刪除名稱按字典由低到高排序成員之間所有成員。 | O(log(N)+M) |
ZREMRANGEBYRANK key start stop | 在排序設(shè)置的所有成員在給定的索引中刪除 | O(log(N)+M) |
ZREMRANGEBYSCORE key min max | 刪除一個排序的設(shè)置在給定的分?jǐn)?shù)所有成員 | O(log(N)+M) |
ZREVRANGE key start stop [WITHSCORES] | 在排序的設(shè)置返回的成員范圍疫粥,通過索引茬斧,下令從分?jǐn)?shù)高到低 | O(log(N)+M) |
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] | 返回有序集合中指定分?jǐn)?shù)區(qū)間內(nèi)的成員,分?jǐn)?shù)由高到低排序梗逮。 | O(log(N)+M) |
ZREVRANK key member | 確定指數(shù)在排序集的成員项秉,下令從分?jǐn)?shù)高到低 | O(log(N)) |
ZSCORE key member | 獲取成員在排序設(shè)置相關(guān)的比分 | O(1) |
ZUNIONSTORE | 添加多個排序集和導(dǎo)致排序的設(shè)置存儲在一個新的鍵 | O(N)+O(M log(M)) |
ZSCAN key cursor [MATCH pattern] [COUNT count] | 迭代sorted sets里面的元素 | O(1) |
2?? 命令的時間復(fù)雜度
參考上表。
3?? 使用場景
有序集合比較典型的使用場景就是排行榜系統(tǒng)慷彤。例如視頻網(wǎng)站需要對用戶上傳的視頻做排行榜娄蔼,榜單的維度可能是多個方面的:按照時間、按照播放數(shù)量底哗、按照獲得的贊數(shù)岁诉。
如上熱搜榜,以日期為 key :
① 點(diǎn)擊熱搜跋选,每次加 1
zincrby hot:20211220 1 薇婭逃稅
② 右側(cè)排行實(shí)現(xiàn)涕癣,展示今日前 50 排名
# zrange 是從低到高返回,zrevrange 反之
zrevrange hot:20211221 0 49 withscores
本文由博客群發(fā)一文多發(fā)等運(yùn)營工具平臺 OpenWrite 發(fā)布