redis
1.redis含義
redis是一個(gè)開源的使用ANSI C編寫、遵循BSD協(xié)議(允許使用者二次開發(fā))、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可基于持久化的日志型、Key-Value、提供多種語言的API的非關(guān)系型數(shù)據(jù)庫。
1.1 優(yōu)點(diǎn)
1.1.1 讀寫性能優(yōu)異, Redis能讀的速度是110000次/s,寫的速度是81000次/s。
1.1.2 支持?jǐn)?shù)據(jù)持久化,支持AOF和RDB兩種持久化方式。
1.1.3 支持事務(wù),Redis的所有操作都是原子性的,同時(shí)Redis還支持對(duì)幾個(gè)操作合并后的原子性執(zhí)行。
1.1.4 數(shù)據(jù)結(jié)構(gòu)豐富,除了支持string類型的value外還支持hash、set、zset、list等數(shù)據(jù)結(jié)構(gòu)。
1.1.5 支持主從復(fù)制,主機(jī)會(huì)自動(dòng)將數(shù)據(jù)同步到從機(jī),可以進(jìn)行讀寫分離。
1.2 缺點(diǎn)
1.2.1 數(shù)據(jù)庫容量受到物理內(nèi)存的限制,不能用作海量數(shù)據(jù)的高性能讀寫,因此Redis適合的場(chǎng)景主要局限在較小數(shù)據(jù)量的高性能操作和運(yùn)算上。
1.2.2 Redis 不具備自動(dòng)容錯(cuò)和恢復(fù)功能,主機(jī)從機(jī)的宕機(jī)都會(huì)導(dǎo)致前端部分讀寫請(qǐng)求失敗,需要等待機(jī)器重啟或者手動(dòng)切換前端的IP才能恢復(fù)。
1.2.3 主機(jī)宕機(jī),宕機(jī)前有部分?jǐn)?shù)據(jù)未能及時(shí)同步到從機(jī),切換IP后還會(huì)引入數(shù)據(jù)不一致的問題,降低了系統(tǒng)的可用性。
1.2.4 Redis 較難支持在線擴(kuò)容,在集群容量達(dá)到上限時(shí)在線擴(kuò)容會(huì)變得很復(fù)雜。為避免這一問題,運(yùn)維人員在系統(tǒng)上線時(shí)必須確保有足夠的空間,這對(duì)資源造成了很大的浪費(fèi)。
2.redis支持的數(shù)據(jù)類型

2.1 String(字符串)
string類型是Redis最基本的數(shù)據(jù)類型,一個(gè)鍵最大能存儲(chǔ)512MB。
string類型是二進(jìn)制安全的。意思是redis的string可以包含任何數(shù)據(jù)。比如jpg圖片或者序列化的對(duì)象 。
2.2 List(列表)
List是簡(jiǎn)單的字符串列表,按照插入順序排序。
2.3 Set(集合)
Set是string類型的無序集合。
2.4 Hash(哈希)
hash是一個(gè)string類型的field和value的映射表,hash特別適合用于存儲(chǔ)對(duì)象。
2.5 zSet(sorted set:有序集合)
zset 和 set 一樣也是string類型元素的集合,且不允許重復(fù)的成員。
不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè)double類型的分?jǐn)?shù)。redis正是通過分?jǐn)?shù)來為集合中的成員進(jìn)行從小到大的排序。
zset的成員是唯一的,但分?jǐn)?shù)(score)卻可以重復(fù)。
3.redis的持久化
持久化是指將內(nèi)存中的數(shù)據(jù)寫入磁盤中,防止服務(wù)器宕機(jī)導(dǎo)致數(shù)據(jù)丟失。
redis提供兩種持久化方式:RDB(默認(rèn))和AOF。
3.1 RDB
rdb是Redis DataBase縮寫
功能核心函數(shù)rdbSave(生成RDB文件)和rdbLoad(從文件加載內(nèi)存)兩個(gè)函數(shù)

3.2 AOF
AOF是Append-only file縮寫

每當(dāng)執(zhí)行服務(wù)器(定時(shí))任務(wù)或者函數(shù)時(shí)flushAppendOnlyFile 函數(shù)都會(huì)被調(diào)用, 這個(gè)函數(shù)執(zhí)行以下兩個(gè)工作
aof寫入保存:
WRITE:根據(jù)條件,將 緩存寫入到 AOF 文件
SAVE:根據(jù)條件,調(diào)用 fsync 或 fdatasync 函數(shù),將 AOF 文件保存到磁盤中
比較:
1、AOF文件比RDB更新頻率高,優(yōu)先使用aof還原數(shù)據(jù)。
2、AOF比RDB更安全也更大
3、RDB性能比AOF好
4、如果兩個(gè)都配了優(yōu)先加載AOF
4.redis通訊協(xié)議
RESP 是redis客戶端和服務(wù)端之前使用的一種通訊協(xié)議;
RESP 的特點(diǎn):實(shí)現(xiàn)簡(jiǎn)單、快速解析、可讀性好。
5.redis的構(gòu)架模式
5.1 單機(jī)版

5.2 主從復(fù)制

主服務(wù)器就會(huì)一直將發(fā)生在自己身上的數(shù)據(jù)更新同步 給從服務(wù)器,從而一直保證主從服務(wù)器的數(shù)據(jù)相同。
缺點(diǎn):
1、無法保證高可用
2、沒有解決 master 寫的壓力
5.3 哨兵

Redis sentinel 是一個(gè)分布式系統(tǒng)中監(jiān)控 redis 主從服務(wù)器,并在主服務(wù)器下線時(shí)自動(dòng)進(jìn)行故障轉(zhuǎn)移。其中三個(gè)特性:
監(jiān)控(Monitoring): Sentinel 會(huì)不斷地檢查你的主服務(wù)器和從服務(wù)器是否運(yùn)作正常。
提醒(Notification): 當(dāng)被監(jiān)控的某個(gè) Redis 服務(wù)器出現(xiàn)問題時(shí), Sentinel 可以通過 API 向管理員或者其他應(yīng)用程序發(fā)送通知。
自動(dòng)故障遷移(Automatic failover): 當(dāng)一個(gè)主服務(wù)器不能正常工作時(shí), Sentinel 會(huì)開始一次自動(dòng)故障遷移操作。
配置中心:如果故障轉(zhuǎn)移發(fā)生了,通知 client 客戶端新的 master 地址。
特點(diǎn):
1、保證高可用
2、監(jiān)控各個(gè)節(jié)點(diǎn)
3、自動(dòng)故障遷移
缺點(diǎn):
1、主從模式,切換需要時(shí)間丟數(shù)據(jù)
2、沒有解決 master 寫的壓力
5.4 proxy集群

Twemproxy 是一個(gè) Twitter 開源的一個(gè) redis 和 memcache 快速/輕量級(jí)代理服務(wù)器; Twemproxy 是一個(gè)快速的單線程代理程序,支持 Memcached ASCII 協(xié)議和 redis 協(xié)議。
特點(diǎn):
1、多種 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
2、支持失敗節(jié)點(diǎn)自動(dòng)刪除
3、后端 Sharding 分片邏輯對(duì)業(yè)務(wù)透明,業(yè)務(wù)方的讀寫方式和操作單個(gè) Redis 一致
缺點(diǎn):
1、增加了新的 proxy,需要維護(hù)其高可用。
2、failover 邏輯需要自己實(shí)現(xiàn),其本身不能支持故障的自動(dòng)轉(zhuǎn)移可擴(kuò)展性差,進(jìn)行擴(kuò)縮容都需要手動(dòng)干預(yù)
5.5 直連型集群

從redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用無中心結(jié)構(gòu),每個(gè)節(jié)點(diǎn)保存數(shù)據(jù)和整個(gè)集群狀態(tài),每個(gè)節(jié)點(diǎn)都和其他所有節(jié)點(diǎn)連接。
特點(diǎn):
1、無中心架構(gòu)(不存在哪個(gè)節(jié)點(diǎn)影響性能瓶頸),少了 proxy 層。
2、數(shù)據(jù)按照 slot 存儲(chǔ)分布在多個(gè)節(jié)點(diǎn),節(jié)點(diǎn)間數(shù)據(jù)共享,可動(dòng)態(tài)調(diào)整數(shù)據(jù)分布。
3、可擴(kuò)展性,可線性擴(kuò)展到 1000 個(gè)節(jié)點(diǎn),節(jié)點(diǎn)可動(dòng)態(tài)添加或刪除。
4、高可用性,部分節(jié)點(diǎn)不可用時(shí),集群仍可用。通過增加 Slave 做備份數(shù)據(jù)副本
5、實(shí)現(xiàn)故障自動(dòng) failover,節(jié)點(diǎn)之間通過 gossip 協(xié)議交換狀態(tài)信息,用投票機(jī)制完成 Slave到 Master 的角色提升。
缺點(diǎn):
1、資源隔離性較差,容易出現(xiàn)相互影響的情況
2、數(shù)據(jù)通過異步復(fù)制,不保證數(shù)據(jù)的強(qiáng)一致性
6.redis常用命令
6.1 Set
設(shè)置 key 對(duì)應(yīng)的值為 string 類型的 value。
6.2 setnx
設(shè)置 key 對(duì)應(yīng)的值為 string 類型的 value。如果 key 已經(jīng)存在,返回 0,nx 是 not exist 的意思。
刪除某個(gè)key
第一次返回1 刪除了 第二次返回0
6.3 Expire 設(shè)置過期時(shí)間(單位秒)
6.4 TTL查看剩下多少時(shí)間
返回負(fù)數(shù)則key失效,key不存在了
6.5 Setex
設(shè)置 key 對(duì)應(yīng)的值為 string 類型的 value,并指定此鍵值對(duì)應(yīng)的有效期。
6.6 Mset
一次設(shè)置多個(gè) key 的值,成功返回 ok 表示所有的值都設(shè)置了,失敗返回 0 表示沒有任何值被設(shè)置。
6.7 Getset
設(shè)置 key 的值,并返回 key 的舊值。
6.8 Mget
一次獲取多個(gè) key 的值,如果對(duì)應(yīng) key 不存在,則對(duì)應(yīng)返回 nil。
6.9 Incr
對(duì) key 的值做加加操作,并返回新的值。注意 incr 一個(gè)不是 int 的 value 會(huì)返回錯(cuò)誤,incr 一個(gè)不存在的 key,則設(shè)置 key 為 1
6.10 incrby
同 incr 類似,加指定值 ,key 不存在時(shí)候會(huì)設(shè)置 key,并認(rèn)為原來的 value 是 0
6.11 Decr
對(duì) key 的值做的是減減操作,decr 一個(gè)不存在 key,則設(shè)置 key 為-1
6.12 Decrby
同 decr,減指定值。
6.13 Append
給指定 key 的字符串值追加 value,返回新字符串值的長(zhǎng)度。
6.14 Strlen
取指定 key 的 value 值的長(zhǎng)度。
6.15 persist xxx(取消過期時(shí)間)
6.16 Select 0 //選擇數(shù)據(jù)庫
6.17 move age 1//把a(bǔ)ge 移動(dòng)到1庫
6.18 Randomkey隨機(jī)返回一個(gè)key
6.19 Rename重命名
6.20 Type 返回?cái)?shù)據(jù)類型
7.redis實(shí)現(xiàn)分布式鎖
先拿setnx來爭(zhēng)搶鎖,搶到之后,再用expire給鎖加一個(gè)過期時(shí)間防止鎖忘記了釋放。
8.redis實(shí)現(xiàn)異步隊(duì)列
一般使用list結(jié)構(gòu)作為隊(duì)列,rpush生產(chǎn)消息,lpop消費(fèi)消息。當(dāng)lpop沒有消息的時(shí)候,要適當(dāng)sleep一會(huì)再重試。
缺點(diǎn):
在消費(fèi)者下線的情況下,生產(chǎn)的消息會(huì)丟失,得使用專業(yè)的消息隊(duì)列如rabbitmq等。
9.緩存穿透、緩存雪崩
9.1 緩存穿透
一般的緩存系統(tǒng),都是按照key去緩存查詢,如果不存在對(duì)應(yīng)的value,就應(yīng)該去后端系統(tǒng)查找(比如DB)。一些惡意的請(qǐng)求會(huì)故意查詢不存在的key,請(qǐng)求量很大,就會(huì)對(duì)后端系統(tǒng)造成很大的壓力。這就叫做緩存穿透。
如何避免?
1:對(duì)查詢結(jié)果為空的情況也進(jìn)行緩存,緩存時(shí)間設(shè)置短一點(diǎn),或者該key對(duì)應(yīng)的數(shù)據(jù)insert了之后清理緩存。
2:對(duì)一定不存在的key進(jìn)行過濾??梢园阉械目赡艽嬖诘膋ey放到一個(gè)大的Bitmap中,查詢時(shí)通過該bitmap過濾。
9.2 緩存雪崩
當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一個(gè)時(shí)間段失效,這樣在失效的時(shí)候,會(huì)給后端系統(tǒng)帶來很大壓力。導(dǎo)致系統(tǒng)崩潰。
如何避免?
1:在緩存失效后,通過加鎖或者隊(duì)列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量。比如對(duì)某個(gè)key只允許一個(gè)線程查詢數(shù)據(jù)和寫緩存,其他線程等待。
2:做二級(jí)緩存,A1為原始緩存,A2為拷貝緩存,A1失效時(shí),可以訪問A2,A1緩存失效時(shí)間設(shè)置為短期,A2設(shè)置為長(zhǎng)期
3:不同的key,設(shè)置不同的過期時(shí)間,讓緩存失效的時(shí)間點(diǎn)盡量均勻。
10 Redis為什么這么快
1、完全基于內(nèi)存,絕大部分請(qǐng)求是純粹的內(nèi)存操作,非??焖?。數(shù)據(jù)存在內(nèi)存中,類似于 HashMap,HashMap 的優(yōu)勢(shì)就是查找和操作的時(shí)間復(fù)雜度都是0/1;
2、數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單,對(duì)數(shù)據(jù)操作也簡(jiǎn)單,Redis 中的數(shù)據(jù)結(jié)構(gòu)是專門進(jìn)行設(shè)計(jì)的;
3、采用單線程,避免了不必要的上下文切換和競(jìng)爭(zhēng)條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能消耗;
4、使用多路 I/O 復(fù)用模型,非阻塞 IO;
5、Redis 直接自己構(gòu)建了 VM 機(jī)制 ,因?yàn)橐话愕南到y(tǒng)調(diào)用系統(tǒng)函數(shù)的話,會(huì)浪費(fèi)一定的時(shí)間去移動(dòng)和請(qǐng)求。
11 Redis持久化數(shù)據(jù)和緩存怎么做擴(kuò)容?
如果Redis被當(dāng)做緩存使用,使用一致性哈希實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)容縮容。
如果Redis被當(dāng)做一個(gè)持久化存儲(chǔ)使用,必須使用固定的keys-to-nodes映射關(guān)系,節(jié)點(diǎn)的數(shù)量一旦確定不能變化。否則的話(即Redis節(jié)點(diǎn)需要?jiǎng)討B(tài)變化的情況),必須使用可以在運(yùn)行時(shí)進(jìn)行數(shù)據(jù)再平衡的一套系統(tǒng),而當(dāng)前只有Redis集群可以做到這樣。
12 Redis的過期鍵的刪除策略
過期策略通常有以下三種:
12.1 定時(shí)過期:
每個(gè)設(shè)置過期時(shí)間的key都需要?jiǎng)?chuàng)建一個(gè)定時(shí)器,到過期時(shí)間就會(huì)立即清除。該策略可以立即清除過期的數(shù)據(jù),對(duì)內(nèi)存很友好;但是會(huì)占用大量的CPU資源去處理過期的數(shù)據(jù),從而影響緩存的響應(yīng)時(shí)間和吞吐量。
12.2 惰性過期:
只有當(dāng)訪問一個(gè)key時(shí),才會(huì)判斷該key是否已過期,過期則清除。該策略可以最大化地節(jié)省CPU資源,卻對(duì)內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問,從而不會(huì)被清除,占用大量?jī)?nèi)存。
12.3 定期過期:
每隔一定的時(shí)間,會(huì)掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key,并清除其中已過期的key。該策略是前兩者的一個(gè)折中方案。通過調(diào)整定時(shí)掃描的時(shí)間間隔和每次掃描的限定耗時(shí),可以在不同情況下使得CPU和內(nèi)存資源達(dá)到最優(yōu)的平衡效果。
(expires字典會(huì)保存所有設(shè)置了過期時(shí)間的key的過期時(shí)間數(shù)據(jù),其中,key是指向鍵空間中的某個(gè)鍵的指針,value是該鍵的毫秒精度的UNIX時(shí)間戳表示的過期時(shí)間。鍵空間是指該Redis集群中保存的所有鍵。)
Redis中同時(shí)使用了惰性過期和定期過期兩種過期策略。
13.Redis事務(wù)的概念
Redis 事務(wù)的本質(zhì)是通過MULTI、EXEC、WATCH等一組命令的集合。事務(wù)支持一次執(zhí)行多個(gè)命令,一個(gè)事務(wù)中所有命令都會(huì)被序列化。在事務(wù)執(zhí)行過程,會(huì)按照順序串行化執(zhí)行隊(duì)列中的命令,其他客戶端提交的命令請(qǐng)求不會(huì)插入到事務(wù)執(zhí)行命令序列中。
Redis 是單進(jìn)程程序,并且它保證在執(zhí)行事務(wù)時(shí),不會(huì)對(duì)事務(wù)進(jìn)行中斷,事務(wù)可以運(yùn)行直到執(zhí)行完所有事務(wù)隊(duì)列中的命令為止。因此,Redis 的事務(wù)是總是帶有隔離性的。
Redis中,單條命令是原子性執(zhí)行的,但事務(wù)不保證原子性,且沒有回滾。事務(wù)中任意命令執(zhí)行失敗,其余的命令仍會(huì)被執(zhí)行。
總結(jié)說:redis事務(wù)就是一次性、順序性、排他性的執(zhí)行一個(gè)隊(duì)列中的一系列命令。