概述
Redis是一個(gè)非關(guān)系型數(shù)據(jù)庫(kù)澈驼,以鍵值對(duì)的方式來(lái)存儲(chǔ)數(shù)據(jù)胶征。
數(shù)據(jù)通過(guò)存儲(chǔ)在內(nèi)存中來(lái)獲得高的讀寫(xiě)性能,同時(shí)也可以存儲(chǔ)到硬盤(pán)以實(shí)現(xiàn)持久化受葛,既適合用作緩存悦陋,也適合用作數(shù)據(jù)存儲(chǔ)蜈彼。
非關(guān)系型數(shù)據(jù)庫(kù)的本質(zhì),就是切掉一部分關(guān)系型數(shù)據(jù)庫(kù)擁有的功能俺驶,而專(zhuān)注于高效的實(shí)現(xiàn)某些功能幸逆。
如在本筆記中發(fā)現(xiàn)錯(cuò)誤,歡迎指正暮现。
參考書(shū)籍
《Redis實(shí)戰(zhàn)》还绘,人民郵電出版社。
數(shù)據(jù)類(lèi)型與命令
Redis可以存儲(chǔ)鍵與5種不同數(shù)據(jù)結(jié)構(gòu)類(lèi)型之間的映射栖袋。
每種類(lèi)型的指令均由該類(lèi)型的首字母開(kāi)頭拍顷,然后用操作對(duì)應(yīng)的英文單詞表達(dá)。
STRING
可以是字符串塘幅、整數(shù)或者浮點(diǎn)數(shù)昔案。
- 整數(shù)的取值范圍和系統(tǒng)的長(zhǎng)整數(shù)的取值范圍相同。
- 浮點(diǎn)數(shù)的取值范圍和精度與IEEE754標(biāo)準(zhǔn)的雙精度浮點(diǎn)數(shù)相同晌块。
數(shù)操作命令
對(duì)數(shù)類(lèi)型進(jìn)行增減的命令。
INCR帅霜、DECR匆背、INCRBY、DECRBY身冀、INCRBYFLOAT钝尸。
- 對(duì)一個(gè)不存在或者保存了空串的鍵進(jìn)行了操作括享,會(huì)將該鍵的的值當(dāng)做0來(lái)處理。
- 如果嘗試對(duì)無(wú)法解釋為整數(shù)或者浮點(diǎn)數(shù)的字符串操作珍促,會(huì)返回錯(cuò)誤铃辖。
子串和二進(jìn)制位命令
對(duì)字符串類(lèi)型進(jìn)行字符或者位級(jí)別操作的指令。
APPEND猪叙、GETRANGE娇斩、SETRANGE、GETBIT穴翩、SETBIT犬第、BITCOUNT、BITOP芒帕。
- 在使用SET*對(duì)字符串進(jìn)行寫(xiě)入的時(shí)候歉嗓,如果字符串長(zhǎng)度不滿(mǎn)足要求,會(huì)自動(dòng)使用空自己(null)來(lái)將字符串?dāng)U展至所需的長(zhǎng)度再執(zhí)行背蟆。
- 在使用GETRANGE讀取字符串時(shí)鉴分,超出字符串末尾的數(shù)據(jù)會(huì)被視為空串。
- 在使用GETBIT讀取二進(jìn)制串時(shí)带膀,超過(guò)字符串末尾的數(shù)據(jù)會(huì)被視為0志珍。
LIST
一個(gè)鏈表,鏈表每個(gè)節(jié)點(diǎn)都包含了一個(gè)字符串本砰。
常用列表命令
推入碴裙、彈出、索引等命令点额。
RPUSH舔株、LPUSH、RPOP还棱、LPOP载慈、LINDEX、LRANGE珍手、LTRIM办铡。
阻塞命令
阻塞命令可以阻塞執(zhí)行命令的客戶(hù)端,直到其他客戶(hù)端給列表添加元素為止琳要,常用于消息傳遞和任務(wù)隊(duì)列寡具。
BLPOP、BRPOP稚补、BPOPLPUSH童叠、BRPOPLPUSH。
SET
包含字符串的無(wú)序收集器课幕,包含的每個(gè)字符串都是獨(dú)一無(wú)二的厦坛。
常用集合命令
添加五垮、刪除、查詢(xún)等命令杜秸。
SADD放仗、SREM、SISMEMBER撬碟、SCARD诞挨、SMEMBERS、SRANDMEMBER小作、SPOP亭姥、SMOVE。
多集合操作命令
用于作集合的交集顾稀、差集达罗、并集等命令。
SDIFF静秆、SDIFFSTORE粮揉、SINTER、SINTERSTORE抚笔、SUNION扶认、SUNIONSTORE。
HASH
包含鍵值對(duì)的無(wú)序散列表殊橙。
- 與字符串一樣辐宾,對(duì)一個(gè)不存在的鍵進(jìn)行自增時(shí),會(huì)將值當(dāng)做0來(lái)處理膨蛮。
常用散列命令
添加叠纹、刪除、查詢(xún)等命令敞葛。
HMGET誉察、HMSET、HDEL惹谐、HLEN持偏。
高級(jí)散列命令
批量操作命令,以及一些字符串操作類(lèi)似的散列命令氨肌。
HEXISTS鸿秆、HKEYS、HVALS怎囚、HGETALL卿叽、HINCRBY、HINCRBYFLOAT。
ZEST
字符串成員與浮點(diǎn)數(shù)分值之間的有序映射附帽,元素的排列順序由分值的大小決定。當(dāng)分值相同時(shí)井誉,會(huì)按照字符串進(jìn)行排序蕉扮。
常用有序集合命令
添加、刪除颗圣、查詢(xún)喳钟、統(tǒng)計(jì)等命令。
ZADD在岂、ZREM奔则、ZCARD、ZINCRBY蔽午、ZCOUNT易茬、ZRANK、ZSCORE及老、ZRANGE抽莱。
范圍操作命令
逆序、子集骄恶、交集食铐、并集等命令。
ZREVRANK僧鲁、ZREVRANGE虐呻、ZRANGEBYSCORE、ZREVRANGEBYSCORE寞秃、ZREMRANGEBYRANK斟叼、ZREMRAMGEBYSCORE、ZINTERSTORE蜕该、ZUNIONSTORE犁柜。
- 交集和并集運(yùn)算需要對(duì)分?jǐn)?shù)使用聚合函數(shù),默認(rèn)函數(shù)是sum堂淡,此外還有min馋缅、max等。
- 普通集合的分值默認(rèn)當(dāng)做1來(lái)處理绢淀。
發(fā)布與訂閱
訂閱者負(fù)責(zé)訂閱頻道萤悴,發(fā)送者負(fù)責(zé)向頻道發(fā)送二進(jìn)制字符串消息。
Redis的發(fā)布訂閱模式有兩個(gè)缺點(diǎn):
- 讀取消息的速度如果趕不上發(fā)送消息的速度皆的,會(huì)被自動(dòng)斷開(kāi)連接覆履。
- 遇到網(wǎng)絡(luò)斷線(xiàn)時(shí)期間的消息都會(huì)丟失。
常用命令
訂閱與發(fā)布的命令。
SUBSCRIBE硝全、UNSUBSCRIBE栖雾、PUBLISH、PSUBSCRIBE伟众、PUNSUBSCRIBE析藕。
其他通用指令
SORT
可以設(shè)置升降序,字符凳厢、數(shù)字账胧、或者其他權(quán)重排序。
過(guò)期時(shí)間
設(shè)置先紫、查看治泥、取消鍵的過(guò)期時(shí)間,但是對(duì)于列表遮精、集合居夹、散列和有序集合,只能為整個(gè)鍵設(shè)置過(guò)期時(shí)間本冲。
PERSIST吮播、TTL、EXPIRE眼俊、EXPIREAT意狠、PTTL、PEXPIRE疮胖、PEXPIREAT环戈。
持久化
Redis提供兩種不同形式的持久化方法,這兩種方法可以不使用澎灸、單獨(dú)使用或者同時(shí)使用:
- 快照院塞,可以將存在于某一時(shí)刻的所有數(shù)據(jù)都寫(xiě)入硬盤(pán)。
- 發(fā)送BGSAVE命令性昭,redis會(huì)用fork創(chuàng)建一個(gè)子進(jìn)程負(fù)責(zé)將快照寫(xiě)入硬盤(pán)拦止。如果系統(tǒng)剩余的內(nèi)存很少,該命令可能導(dǎo)致系統(tǒng)長(zhǎng)時(shí)間停頓糜颠。
- 發(fā)送SAVE命令汹族,redis將開(kāi)始創(chuàng)建快照并不再接收任何命令,直到快照創(chuàng)建完畢其兴。因?yàn)椴挥煤妥舆M(jìn)程爭(zhēng)搶資源顶瞒,所以該命令創(chuàng)建快照的速度會(huì)比BGSAVE更快。
- 通過(guò)SHUTDOWN命令接收關(guān)閉服務(wù)器請(qǐng)求時(shí)元旬,會(huì)執(zhí)行SAVE命令榴徐,執(zhí)行完畢后再關(guān)閉服務(wù)器守问。
- 服務(wù)崩潰會(huì)導(dǎo)致最后一次快照之后的數(shù)據(jù)丟失。
- 從服務(wù)器連接到主服務(wù)器并發(fā)送SYNC命令時(shí)坑资,如果主服務(wù)器沒(méi)有在執(zhí)行BGSAVE命令耗帕,則會(huì)執(zhí)行該命令。
- 只追加文件袱贮,在執(zhí)行寫(xiě)命令時(shí)將被執(zhí)行的寫(xiě)命令復(fù)制到硬盤(pán)里面兴垦。
- appendfsync選項(xiàng)用于設(shè)置同步頻率:
- no:操作系統(tǒng)決定,幾乎不對(duì)redis性能帶來(lái)影響字柠,系統(tǒng)崩潰會(huì)丟失不定量數(shù)據(jù),如果硬盤(pán)寫(xiě)入速度不夠快導(dǎo)致緩沖區(qū)被填滿(mǎn)狡赐,會(huì)阻塞redis寫(xiě)入窑业。
- everysec:每秒一次,對(duì)redis性能影響較小枕屉。
- always:每次寫(xiě)命令執(zhí)行常柄,受硬盤(pán)性能限制,會(huì)嚴(yán)重降低redis性能搀擂,并減少固態(tài)硬盤(pán)的壽命西潘。
- appendfsync選項(xiàng)用于設(shè)置同步頻率:
- 發(fā)送BGREWRITEAOF命令可以移除AOF文件中的冗余命令來(lái)重寫(xiě)AOF文件以減少文件體積,該命令的工作原理和BGSAVE十分相似哨颂。
復(fù)制
與關(guān)系型數(shù)據(jù)庫(kù)類(lèi)似喷市,通過(guò)主從復(fù)制,客戶(hù)端每次向主服務(wù)器
寫(xiě)入時(shí)威恼,都會(huì)同步到從服務(wù)器品姓,讀請(qǐng)求可以向任意的從服務(wù)器發(fā)送。
Redis不支持主主復(fù)制箫措。
配置
- 為從服務(wù)器設(shè)置slaveof host port配置腹备,redis在啟動(dòng)時(shí)首先會(huì)載入當(dāng)前可用的任何快照或者AOF文件,然后連接主服務(wù)器斤蔓。
- 使用SLAVEOF命令植酥,redis會(huì)立即嘗試連接主服務(wù)器。
- 從服務(wù)器默認(rèn)是不允許寫(xiě)入的弦牡,但某些查詢(xún)可能需要進(jìn)行一些臨時(shí)寫(xiě)入友驮,此時(shí)需要將從服務(wù)器的salve-read-only配置選項(xiàng)從默認(rèn)的yes改成no。
復(fù)制過(guò)程
- 從服務(wù)器發(fā)送SYNC命令連接主服務(wù)器驾锰。
- 主服務(wù)器執(zhí)行BGSAVE喊儡。
- 主服務(wù)器發(fā)送快照文件并使用緩沖區(qū)記錄這段期間的寫(xiě)命令,從服務(wù)器丟棄所有舊數(shù)據(jù)并載入主服務(wù)器的快照文件稻据。
- 主服務(wù)器發(fā)送緩沖區(qū)的命令艾猜。
- 主服務(wù)器同步發(fā)送寫(xiě)命令买喧。
對(duì)于主從鏈復(fù)制,如果從服務(wù)器X擁有從服務(wù)器Y匆赃,那么當(dāng)X執(zhí)行對(duì)快照文件的解釋時(shí)淤毛,會(huì)斷開(kāi)與Y的連接,導(dǎo)致Y需要重新連接和同步算柳。
故障處理
驗(yàn)證快照文件或者AOF文件
redis提供了兩個(gè)命令行程序來(lái)檢查快照文件和AOF文件的狀態(tài)并在需要的情況下進(jìn)行恢復(fù):
- redis-check-aof:redis會(huì)掃描aof文件低淡,當(dāng)發(fā)現(xiàn)第一個(gè)出錯(cuò)命令時(shí),會(huì)刪除出錯(cuò)的命令以及位于出錯(cuò)命令后的所有命令瞬项。
- redis-check-dump
更換redis服務(wù)器
更換主服務(wù)器
因?yàn)閺姆?wù)器是無(wú)法執(zhí)行寫(xiě)命令的蔗蹋,所以可以通過(guò)快照的方式發(fā)送數(shù)據(jù),整個(gè)過(guò)程不會(huì)出現(xiàn)數(shù)據(jù)的不一致性囱淋。
- 從服務(wù)器執(zhí)行SAVE命令生成快照猪杭。
- 將快照文件發(fā)送給新的主服務(wù)器,讓主服務(wù)器從快照啟動(dòng)妥衣。
- 將從服務(wù)器連接上新的主服務(wù)器皂吮。
升級(jí)從服務(wù)器
修改配置文件讓從服務(wù)器升級(jí)成為主服務(wù)器。
事務(wù)
與關(guān)系型數(shù)據(jù)庫(kù)的差異
redis的事務(wù)和傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)不同税手。
- 關(guān)系型數(shù)據(jù)庫(kù)事務(wù)以BEGIN命令開(kāi)始蜂筹,然后執(zhí)行各個(gè)相互一致的讀寫(xiě)操作,最后用戶(hù)可以選擇COMMIT確認(rèn)修改或者ROLLBACK放棄修改芦倒。
- redis事務(wù)以MULTI命令開(kāi)始艺挪,然后傳入多個(gè)命令,最后以EXEC結(jié)束兵扬,但是在EXEC命令被調(diào)用前不會(huì)執(zhí)行任何實(shí)際操作闺属,所以用戶(hù)沒(méi)法根據(jù)讀取到的數(shù)據(jù)來(lái)決定。
命令
redis事務(wù)由五個(gè)命令組成:
- MULTI:事務(wù)開(kāi)始的命令周霉。
- EXEC:事務(wù)結(jié)束的命令掂器。
- WATCH:對(duì)鍵進(jìn)行監(jiān)視,在用戶(hù)執(zhí)行EXEC之前俱箱,如果有其他客戶(hù)對(duì)被監(jiān)視的鍵進(jìn)行了修改国瓮,那么事務(wù)將失敗并返回錯(cuò)誤。
- UNWATCH:在WATCH命令之后狞谱、MULTI命令之前對(duì)連接進(jìn)行重置乃摹。
- DISCARD:在MULTI命令執(zhí)行之后、EXEC命令執(zhí)行之前對(duì)連接進(jìn)行重置跟衅,該指令會(huì)取消WATCH命令孵睬,并且清空已入隊(duì)的命令。
客戶(hù)端流水線(xiàn)
- 客戶(hù)端往往會(huì)用隊(duì)列緩存指令伶跷,直到輸入EXEC命令后再一次性將事務(wù)發(fā)送給服務(wù)器掰读,以減少通信數(shù)量秘狞,提高速度。
- 非事務(wù)型操作也可以使用流水線(xiàn)的方法來(lái)提高速度蹈集。
鎖
鎖分類(lèi)
在共享數(shù)據(jù)庫(kù)中,通過(guò)加鎖可以讓當(dāng)前客戶(hù)端對(duì)數(shù)據(jù)進(jìn)行排他性訪(fǎng)問(wèn)拢肆。一般步驟為先獲取鎖减响,然后執(zhí)行操作,最后釋放鎖郭怪。
- 常見(jiàn)的關(guān)系型數(shù)據(jù)庫(kù)在加鎖時(shí)就不允許其他客戶(hù)端修改了支示,這種鎖稱(chēng)為“悲觀鎖”。
- Redis使用WATCH命令來(lái)對(duì)數(shù)據(jù)進(jìn)行加鎖鄙才,WATCH只會(huì)在數(shù)據(jù)被其他客戶(hù)端搶先修改了的情況下才通知執(zhí)行了這個(gè)命令的客戶(hù)端颂鸿,而不會(huì)阻止其他客戶(hù)端對(duì)數(shù)據(jù)進(jìn)行修改,這種加鎖方式稱(chēng)為“樂(lè)觀鎖”咒循。
- 因?yàn)樵谑『髸?huì)進(jìn)行重試,所以這種鎖在重負(fù)載的情況下可能會(huì)出現(xiàn)性能問(wèn)題绞愚。
悲觀鎖實(shí)現(xiàn)
- SETNX指令叙甸,只會(huì)在鍵不存在的情況下為鍵設(shè)置值,用于獲取鎖位衩。
- 鎖占有者通過(guò)為鍵設(shè)置超時(shí)時(shí)間裆蒸,來(lái)避免自身崩潰后一直持有鎖。
- 競(jìng)爭(zhēng)者通過(guò)檢查并為沒(méi)有超時(shí)時(shí)間的鎖設(shè)置超時(shí)時(shí)間糖驴,來(lái)避免鎖占有者在設(shè)置超時(shí)時(shí)間之前就崩潰僚祷。
- 鎖占有者通過(guò)取消鍵的值來(lái)釋放鎖。
信號(hào)量
通過(guò)redis命令有多種方式來(lái)實(shí)現(xiàn)信號(hào)量功能贮缕,屬于redis應(yīng)用場(chǎng)景辙谜,本篇不多介紹。
性能優(yōu)化
短結(jié)構(gòu)
- Redis為列表感昼、集合装哆、散列和有序集合提供了一組配置選項(xiàng),可以讓Redis以更節(jié)約空間的壓縮列表存儲(chǔ)長(zhǎng)度較短的結(jié)構(gòu)定嗓,即短結(jié)構(gòu)蜕琴。
- 列表、散列宵溅、有序集合的配置選項(xiàng)由*-max-ziplist-entries和-max-ziplist-value組成凌简,entries表示包含最大元素的數(shù)量,value表示每個(gè)節(jié)點(diǎn)的最大體積字節(jié)數(shù)恃逻。
- 集合的配置選項(xiàng)為set-max-intset-entries雏搂,但要求成員必須能夠被解釋為可表示的整數(shù)藕施,這種存儲(chǔ)方式被稱(chēng)為整數(shù)集合。
- 當(dāng)選項(xiàng)設(shè)置的任意限制條件被突破的時(shí)候畔派,redis會(huì)將編碼轉(zhuǎn)換為其他結(jié)構(gòu)铅碍。
- 壓縮列表會(huì)以序列化的方式存儲(chǔ)數(shù)據(jù),這些序列化數(shù)據(jù)每次被讀取的時(shí)候都要進(jìn)行解碼线椰,每次被寫(xiě)入的時(shí)候也要進(jìn)行局部的重新編碼胞谈,并且可能需要對(duì)內(nèi)存里面的數(shù)據(jù)進(jìn)行移動(dòng)。
- 所以短結(jié)構(gòu)本質(zhì)上是拿時(shí)間換空間憨愉,當(dāng)結(jié)構(gòu)很長(zhǎng)時(shí)烦绳,對(duì)時(shí)間性能的影響就會(huì)變得很大。
分片結(jié)構(gòu)
- 一個(gè)通用的技巧配紫,對(duì)數(shù)據(jù)計(jì)算哈希值后分片存儲(chǔ)径密,既可以對(duì)鍵進(jìn)行分片也可以對(duì)鍵的值進(jìn)行分片。分片后的數(shù)據(jù)進(jìn)行讀寫(xiě)以及運(yùn)算操作都需要進(jìn)行相應(yīng)的調(diào)整躺孝。
- 可以和短結(jié)構(gòu)組合使用享扔,讓每個(gè)分片都能夠被表示為短結(jié)構(gòu)。
- 不同的分片可以存儲(chǔ)在不同的數(shù)據(jù)庫(kù)中從而對(duì)數(shù)據(jù)庫(kù)的寫(xiě)性能進(jìn)行擴(kuò)展植袍。
位圖表示
- 一個(gè)通用的技巧惧眠,對(duì)數(shù)據(jù)進(jìn)行連續(xù)編碼,然后以位圖的方式來(lái)存儲(chǔ)于个。
- 可以和分片與短結(jié)構(gòu)組合使用氛魁,將長(zhǎng)位圖進(jìn)行分片。
Lua腳本編程
Redis從2.6版本開(kāi)始引入使用Lua編程語(yǔ)言進(jìn)行的服務(wù)器端腳本編程功能厅篓。
- SCRIPT LOAD命令接受一個(gè)字符串格式的Lua腳本為參數(shù)秀存,然后把腳本存儲(chǔ)起來(lái)并返回SHA1的校驗(yàn)和。
- EVALSHA命令接受腳本的SHA1校驗(yàn)和以及腳本所需的全部參數(shù)來(lái)執(zhí)行腳本羽氮。
- Redis也把EVAL和EVALSHA看作是單個(gè)命令來(lái)處理或链,都是原子操作。所以通過(guò)Lua腳本可以更加簡(jiǎn)單的實(shí)現(xiàn)事務(wù)操作的效果档押。
注意事項(xiàng)
- Lua里面的某些數(shù)據(jù)類(lèi)型是不允許進(jìn)行傳出的株扛,因?yàn)槟_本在返回不同類(lèi)型的數(shù)據(jù)時(shí)可能會(huì)產(chǎn)生含糊不清的結(jié)果,所以應(yīng)該盡量顯式的返回字符串汇荐,然后手動(dòng)的進(jìn)行分析操作洞就。
- 使用SCRIPT KILL命令可以終止正在運(yùn)行的腳本,但是已經(jīng)對(duì)結(jié)構(gòu)進(jìn)行了修改的Lua腳本將無(wú)法被中斷掀淘,只能使用SHUTDOWN NOSAVE命令殺死服務(wù)器旬蟋,讓服務(wù)器從最近的一次快照重新啟動(dòng)。