Redis學(xué)習(xí)筆記

. 簡(jiǎn)述:

Redis由Salvatore Sanfilippo使用C語(yǔ)言編寫(xiě)的一種支持網(wǎng)絡(luò)课锌、可基于內(nèi)存亦可持久化的日志型挣柬、Key-Value數(shù)據(jù)庫(kù)萌业。

2. 特點(diǎn):

2.1 優(yōu)點(diǎn):

2.1.1 redis的支持?jǐn)?shù)據(jù)持久化

可以將內(nèi)存中的數(shù)據(jù)保存在磁盤(pán)中愿汰,重啟時(shí)通過(guò)再次加載彬碱,這樣數(shù)據(jù)不會(huì)丟失窃诉;

2.1.2 Redis支持?jǐn)?shù)據(jù)類(lèi)型豐富

支持String杨耙,List,Set飘痛,Zset(sorted sets)珊膜,Hash數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ);

2.1.3 Redis支持master-slave模式的數(shù)據(jù)備份宣脉;
2.1.4 Redis的操作支持原子性

Redis提供了簡(jiǎn)單的事務(wù)功能车柠, 將一組需要一起執(zhí)行的命令放到multi和exec兩個(gè)命令之間(其中 multi命令代表事務(wù)開(kāi)始, exec命令代表事務(wù)結(jié)束)塑猖,它們之間的命令是原子順序執(zhí)行的竹祷。

2.1.5 性能極高

讀寫(xiě)數(shù)據(jù)每秒可達(dá)10萬(wàn)次左右。

2.1.6 字符串能夠存儲(chǔ)的最大值是512M羊苟,幾乎能夠滿(mǎn)足所有常見(jiàn)業(yè)務(wù)場(chǎng)景溶褪。

缺點(diǎn):

2.2.1 容量收到物理內(nèi)存的限制,適合小數(shù)據(jù)量級(jí)別的高性能讀寫(xiě)操作践险,大數(shù)據(jù)量可以選擇淘寶的Tair
2.2.2 不具有自動(dòng)容錯(cuò)和恢復(fù)能力猿妈;
2.2.3 主機(jī)宕機(jī)之后,如果有部分沒(méi)有同步到從機(jī)的數(shù)據(jù)巍虫,那么切到從機(jī)之后彭则,會(huì)出現(xiàn)數(shù)據(jù)不一致的情況;
2.2.3 redis事務(wù)不支持回滾占遥。

3.Redis的數(shù)據(jù)持久化

Redis提供了RDB(Redis DataBase)和AOF(Append Only File)兩種持久化方式俯抖。

3.1 RDB是指在一定的時(shí)間間隔將數(shù)據(jù)快照存儲(chǔ)到磁盤(pán)。

當(dāng)?shù)竭_(dá)數(shù)據(jù)備份時(shí)間時(shí)瓦胎,Redis會(huì)啟動(dòng)一個(gè)線(xiàn)程芬萍,該線(xiàn)程將數(shù)據(jù)保存到一個(gè)臨時(shí)文件,以替換上個(gè)時(shí)間周期備份的臨時(shí)文件搔啊。優(yōu)點(diǎn):數(shù)據(jù)恢復(fù)快柬祠,缺點(diǎn):最后一次持久化后的數(shù)據(jù)有可能會(huì)丟失。

3.2 AOF是以日志的形式記錄每次對(duì)Redis的寫(xiě)操作

以redis協(xié)議追加保存每次寫(xiě)的操作到文件末尾负芋,當(dāng)Redis重啟的時(shí)漫蛔,重新執(zhí)行這些命令來(lái)恢復(fù)原始的數(shù)據(jù)。優(yōu)點(diǎn):數(shù)據(jù)實(shí)時(shí)同步,缺點(diǎn):AOF文件持續(xù)增長(zhǎng)而過(guò)大(Redis通過(guò)設(shè)置文件大小的閥值來(lái)觸發(fā)AOF文件的壓縮)莽龟。

注:當(dāng)同時(shí)開(kāi)啟RDB和AOF時(shí)蠕嫁,系統(tǒng)優(yōu)先使用AOF。

4.主從復(fù)制

4.1 主從復(fù)制的延遲

主從復(fù)制可以通過(guò)slaveof命令配置實(shí)現(xiàn)毯盈,默認(rèn)情況下Redis都是主節(jié)點(diǎn)剃毒,每個(gè)從節(jié)點(diǎn)只能有一個(gè)主節(jié)點(diǎn),而主節(jié)點(diǎn)可以有多個(gè)從節(jié)點(diǎn)搂赋,故復(fù)制的數(shù)據(jù)流是單向的迟赃,即只能從主節(jié)點(diǎn)復(fù)制到從節(jié)點(diǎn),因此主節(jié)點(diǎn)無(wú)法感知從節(jié)點(diǎn)的修改厂镇,所以一般從節(jié)點(diǎn)提供只讀模式纤壁。

由于主從部署在不同的機(jī)器上,Redis提供了repl-disable-tcp-nodelay參數(shù)用于控制是否關(guān)閉

TCP_NODELAY捺信, 默認(rèn)關(guān)閉酌媒。
a.當(dāng)關(guān)閉時(shí), 主節(jié)點(diǎn)產(chǎn)生的命令數(shù)據(jù)大小都會(huì)及時(shí)地發(fā)送給從節(jié)點(diǎn)迄靠, 優(yōu)點(diǎn):延遲會(huì)變小秒咨, 缺點(diǎn):網(wǎng)絡(luò)帶寬的消耗增加。 適用于同機(jī)架或同機(jī)房部署等網(wǎng)絡(luò)環(huán)境好的場(chǎng)景掌挚。

b.當(dāng)開(kāi)啟時(shí)雨席, 主節(jié)點(diǎn)會(huì)合并較小的數(shù)據(jù)包發(fā)送到從節(jié)點(diǎn),一般時(shí)間間隔設(shè)置為30-50毫秒吠式。優(yōu)點(diǎn):節(jié)省帶寬陡厘,缺點(diǎn):增大主從之間的延遲。 適用于主從網(wǎng)絡(luò)環(huán)境較差的環(huán)境特占, 如跨機(jī)房部署等糙置。

注意:從節(jié)點(diǎn)更換主節(jié)點(diǎn)后從節(jié)點(diǎn)會(huì)清空之前所有的數(shù)據(jù), 所以執(zhí)行該操作時(shí)一定要在正確的主節(jié)點(diǎn)和從節(jié)點(diǎn)上是目。

4.2 Redis拓?fù)浣Y(jié)構(gòu)

4.2.1 一主一從

4.2.2 一主多從

4.2.3 樹(shù)狀主從

4.3 主從復(fù)制過(guò)程

1.從節(jié)點(diǎn)保存主節(jié)點(diǎn)信息

即執(zhí)行slaveof后谤饭,從節(jié)點(diǎn)保存主節(jié)點(diǎn)的地址信息。

2.主從建立Socket連接

從節(jié)點(diǎn)通過(guò)執(zhí)行定時(shí)任務(wù)(即每秒執(zhí)行一次)發(fā)現(xiàn)slaveof新的主節(jié)點(diǎn)后懊纳,會(huì)嘗試建立Socket網(wǎng)絡(luò)連接揉抵。如果連接失敗,從節(jié)點(diǎn)會(huì)通過(guò)定時(shí)任務(wù)來(lái)無(wú)限重試嗤疯。

3.發(fā)送ping命令

Socket連接成功后冤今,從節(jié)點(diǎn)發(fā)送ping請(qǐng)求進(jìn)行首次通信,主要目的是:檢測(cè)主從之間網(wǎng)絡(luò)套接字是否可用身弊,并檢測(cè)主節(jié)點(diǎn)當(dāng)前是否可接受處理命令辟汰。

4.權(quán)限驗(yàn)證

如果主節(jié)點(diǎn)設(shè)置了requirepass參數(shù)列敲, 則需要密碼驗(yàn)證阱佛,從節(jié)點(diǎn)必須配置masterauth參數(shù)保證與主節(jié)點(diǎn)相同的密碼才能通過(guò)驗(yàn)證帖汞。

5.同步數(shù)據(jù)集

主從復(fù)制連接正常后, 對(duì)于首次建立復(fù)制的場(chǎng)景凑术, 主節(jié)點(diǎn)會(huì)把持有的數(shù)據(jù)全部發(fā)送給從節(jié)點(diǎn)翩蘸,

6.命令持續(xù)復(fù)制

主節(jié)點(diǎn)會(huì)持續(xù)地把寫(xiě)命令發(fā)送給從節(jié)點(diǎn), 從而保證主從數(shù)據(jù)一致淮逊。

4.4 數(shù)據(jù)延遲

Redis主從數(shù)據(jù)的延遲由于異步復(fù)制特性導(dǎo)致的催首,是無(wú)法避免的, 延遲受到網(wǎng)絡(luò)帶寬和命令阻塞情況的影響泄鹏, 如果業(yè)務(wù)不允許短時(shí)間內(nèi)的數(shù)據(jù)延遲郎任, 可以編碼實(shí)現(xiàn)監(jiān)控程序,監(jiān)聽(tīng)主從節(jié)點(diǎn)的復(fù)制偏移量备籽, 當(dāng)延遲較大時(shí)觸發(fā)報(bào)警或通知客戶(hù)端避免讀取延遲過(guò)大的從節(jié)點(diǎn)舶治,從而避免讀不到數(shù)據(jù)的情況發(fā)生。

4.5 過(guò)期數(shù)據(jù)的處理

4.5.1 惰性刪除

主節(jié)點(diǎn)每次處理讀取命令時(shí)车猬,除了讀取key-value表霉猛,還會(huì)讀取Redis超時(shí)時(shí)間表,以檢查key是否超時(shí)珠闰, 如果超時(shí)則刪除key對(duì)象惜浅, 之后del命令也會(huì)異步發(fā)送給從節(jié)點(diǎn),從節(jié)點(diǎn)再執(zhí)行del伏嗜。

4.5.2 定時(shí)刪除

Redis主節(jié)點(diǎn)在內(nèi)部定時(shí)任務(wù)會(huì)循環(huán)讀取一定數(shù)量的Key坛悉, 當(dāng)發(fā)現(xiàn)讀取的Key過(guò)期時(shí)執(zhí)行del命令,之后del命令也會(huì)異步發(fā)送給從節(jié)點(diǎn)承绸,從節(jié)點(diǎn)再執(zhí)行del吹散。

注意:為了保證復(fù)制數(shù)據(jù)的一致性, 從節(jié)點(diǎn)自身不會(huì)主動(dòng)刪除超時(shí)數(shù)據(jù)八酒。

4.6 Redis讀寫(xiě)分離的考量

考慮到數(shù)據(jù)延遲空民,過(guò)期數(shù)據(jù)等,以及Redis的高性能羞迷,因此在考慮讀寫(xiě)分離之前界轩,一定要在主節(jié)點(diǎn)上做好充分優(yōu)化,如解決慢查詢(xún)衔瓮,持久化阻塞和合理的數(shù)據(jù)結(jié)構(gòu)等浊猾,當(dāng)仍然不滿(mǎn)足時(shí),可以先考慮Redis Cluster等分布式方案热鞍,再考慮讀寫(xiě)分離方案葫慎。

5.Redis的阻塞問(wèn)題

由于Redis是典型的單線(xiàn)程架構(gòu)衔彻,所有的讀寫(xiě)操作都在一條主線(xiàn)程中完成,因此如果主線(xiàn)程擁塞了偷办,那將是業(yè)務(wù)系統(tǒng)的噩夢(mèng)艰额。
造成阻塞的原因如下:

5.1 API或數(shù)據(jù)結(jié)構(gòu)使用不合理

問(wèn)題定位:1.通過(guò)slowlog get { n } 獲取最近n條慢查詢(xún)(Redis默認(rèn)對(duì)于執(zhí)行超過(guò)10毫秒的命令都會(huì)記錄到一個(gè)定長(zhǎng)隊(duì)列(隊(duì)列默認(rèn)長(zhǎng)度為128)中),發(fā)現(xiàn)慢查詢(xún)后椒涯,可以通過(guò)使用低算法復(fù)雜度的命令或避免大對(duì)象數(shù)據(jù)來(lái)優(yōu)化柄沮。

5.2 CPU飽和的問(wèn)題

CPU飽和是指Redis把單核CPU(由于Redis是單線(xiàn)程,只能使用一個(gè)CPU)使用率跑到接近100%废岂∽娲辏可以使用top命令查看Redis線(xiàn)程的CPU使用率,可參考:http://www.reibang.com/p/6d7571d82304湖苞。對(duì)于Redis的使用情況拯欧,可通過(guò)redis-cli-h{ip}-p{port}--stat命令每秒輸出Redis的統(tǒng)計(jì)信息。如果此時(shí)發(fā)現(xiàn)Redis的請(qǐng)求量比較低财骨,那說(shuō)明使用了高復(fù)雜度算法的命令镐作;如果此時(shí)發(fā)現(xiàn)Redis的請(qǐng)求量已經(jīng)很高了,而且應(yīng)用的優(yōu)化有限蚓再,那就需要使用新的Redis來(lái)分?jǐn)傉?qǐng)求和CPU的壓力滑肉。

5.3 持久化相關(guān)的阻塞

對(duì)于開(kāi)啟了持久化功能的Redis節(jié)點(diǎn),要檢查是否是fork阻塞摘仅,AOF刷盤(pán)阻塞和HugePage寫(xiě)操作阻塞導(dǎo)致的CPU使用率過(guò)高靶庙。具體可參考官網(wǎng):http://www.redis.io/topics/latency

5.4 CPU競(jìng)爭(zhēng)

當(dāng)Redis和其他多核CPU密集型服務(wù)部署在一起時(shí),其他進(jìn)程過(guò)度消耗CPU時(shí)娃属, 將嚴(yán)重影響Redis吞吐量六荒。可以將Redis綁定到一個(gè)CPU上矾端,避免CPU切換的開(kāi)銷(xiāo)掏击。

5.5 內(nèi)存交換

如果系統(tǒng)把Redis的部分內(nèi)存中的數(shù)據(jù)換出并序列化到磁盤(pán)后,如果請(qǐng)求的數(shù)據(jù)不在內(nèi)存中秩铆,需要內(nèi)存交換砚亭,那么Redis性能?chē)?yán)重下降。解決方式:擴(kuò)大內(nèi)存殴玛,設(shè)置Redis的最大可用內(nèi)存捅膘,降低系統(tǒng)內(nèi)存交換執(zhí)行的優(yōu)先級(jí)。

5.6 網(wǎng)絡(luò)問(wèn)題

解決方式:增加帶寬滚粟,同機(jī)房部署寻仗,機(jī)房部署使用專(zhuān)線(xiàn),改善機(jī)器的物理拓?fù)洌ㄍ锢頇C(jī)>同機(jī)架>跨機(jī)架>同機(jī)房>同城機(jī)房>異地機(jī)房凡壤,注意:容災(zāi)性相反)

6. Redis高性能的原因:

6.1.純內(nèi)存

Redis將所有數(shù)據(jù)放在內(nèi)存中署尤,而內(nèi)存的相應(yīng)時(shí)長(zhǎng)在100納秒左右耙替。

6.2.非阻塞I/O

Redis使用了epoll多路復(fù)用技術(shù),另外Redis自身的事件處理模型將epoll中的連接曹体、 讀寫(xiě)俗扇、關(guān)閉都轉(zhuǎn)換為事件。

6.3.沒(méi)有線(xiàn)程切換(Redis 6已經(jīng)支持了多線(xiàn)程)

Redis采用了單線(xiàn)程架構(gòu)混坞,避免了線(xiàn)程切換狐援、競(jìng)態(tài)和鎖等產(chǎn)生的自我消耗钢坦。

7.Redis的Pipeline流水線(xiàn)

Redis提供了批量操作命令(如mget究孕、 mset、hmset等)爹凹,有效的節(jié)約了多次訪(fǎng)問(wèn)的往返時(shí)間厨诸,但是有些命令沒(méi)有對(duì)應(yīng)的批量命令(如hgetall沒(méi)有對(duì)應(yīng)的mhgetall),如果n次調(diào)用禾酱,就會(huì)消耗n次往返時(shí)間微酬。Redis針對(duì)這一問(wèn)題提供了Pipeline流水線(xiàn)機(jī)制,Redis將一組Redis命令組裝到一起颤陶,通過(guò)一次調(diào)用Redis颗管,再將執(zhí)行結(jié)果按照請(qǐng)求順序返回客戶(hù)端。

8 Redis內(nèi)存優(yōu)化:

info memory命令可以顯示內(nèi)存的相關(guān)指標(biāo)滓走,主要有used_memory(Redis內(nèi)部存儲(chǔ)所有數(shù)據(jù)內(nèi)存占用量)垦江,used_memory_ssr(操作系統(tǒng)顯示Redis進(jìn)程占用的物理內(nèi)存)以及他們的比值mem_fragmentation_ratio。
當(dāng)mem_fragmentation_ratio>1時(shí)搅方,說(shuō)明有部分內(nèi)存沒(méi)有用于數(shù)據(jù)存儲(chǔ)比吭,而是被內(nèi)存碎片消耗,因此要降低mem_fragmentation_ratio的值姨涡,即降低碎片率衩藤。
當(dāng)mem_fragmentation_ratio<1時(shí),是因?yàn)椴僮飨到y(tǒng)把Redis的內(nèi)存數(shù)據(jù)交換到磁盤(pán)涛漂,因此used_memory_ssr才會(huì)大于used_memory赏表,由于硬盤(pán)的讀寫(xiě)速度和內(nèi)存差距很大,此時(shí)Redis的性能會(huì)很差匈仗。

8.2.內(nèi)存的使用劃分

8.2.1 Redis的進(jìn)程自身的內(nèi)存

這部分?jǐn)?shù)據(jù)在幾MB級(jí)別瓢剿,很小可以忽略不計(jì)。

8.2.2 對(duì)象內(nèi)存

即所有key-value數(shù)據(jù)使用的內(nèi)存,包括所有key的消耗和所有value的消耗锚沸。

8.2.3 緩存內(nèi)存

主要包括客戶(hù)端緩存跋选,復(fù)制積壓區(qū)緩存,AOF緩存哗蜈。

8.2.3.1 客戶(hù)端緩存:

指的是所有接入到Redis服務(wù)器TCP連接的輸入輸出緩沖前标。輸入緩沖無(wú)法控制(最大空間為1G)坠韩, 如果超過(guò)將斷開(kāi)連接。 輸出緩沖通過(guò)參數(shù)控制(默認(rèn)為1G)
a.客戶(hù)端:可以通過(guò)設(shè)置最大客戶(hù)端連接數(shù)(特別是有慢連接時(shí))來(lái)限制大量客戶(hù)端接入造成的內(nèi)存消耗
b.主從復(fù)制客戶(hù)端:當(dāng)主從復(fù)制延遲較高或從節(jié)點(diǎn)掛載從節(jié)點(diǎn)數(shù)量過(guò)多時(shí)炼列,內(nèi)存消耗將增加只搁。可通過(guò)改善網(wǎng)絡(luò)環(huán)境和減少主節(jié)點(diǎn)掛載的從節(jié)點(diǎn)數(shù)來(lái)解決俭尖。
c.訂閱客戶(hù)端:使用發(fā)布/訂閱時(shí)氢惋, 連接客戶(hù)端使用獨(dú)立的輸出緩沖區(qū),對(duì)于訂閱消息短時(shí)間內(nèi)量比較大時(shí)稽犁,輸出緩沖區(qū)會(huì)產(chǎn)生積壓溢出焰望。
d 復(fù)制積壓緩存區(qū)
復(fù)制積壓緩存區(qū)主要用來(lái)實(shí)現(xiàn)主從復(fù)制功能,一個(gè)主節(jié)點(diǎn)只有一個(gè)復(fù)制積壓緩存區(qū)已亥,所有從節(jié)點(diǎn)共享該緩存區(qū)熊赖。
e AOF緩沖區(qū)
AOF緩沖區(qū)用于在Redis重寫(xiě)期間保存最近的寫(xiě)入命令,AOF緩存區(qū)的消耗大小由AOF重寫(xiě)時(shí)間和寫(xiě)入命令量決定虑椎,這部分空間占用量比較小震鹉。

8.2.3.2 內(nèi)存碎片

Redis提供了jemalloc(默認(rèn)),glibc和tcmalloc三種內(nèi)存分配器捆姜。當(dāng)存儲(chǔ)的數(shù)據(jù)長(zhǎng)短差異較大時(shí)传趾,做頻繁的更新,刪除大量key泥技。主要解決碎片的方法有:
a.數(shù)據(jù)對(duì)齊浆兰,盡量采用數(shù)字類(lèi)型或者固定長(zhǎng)度字符串等;
b.Redis重啟,內(nèi)存會(huì)重新整理零抬。

8.2.3.3 子進(jìn)程內(nèi)存消耗

子進(jìn)程內(nèi)存消耗主要指執(zhí)行AOF/RDB重寫(xiě)時(shí)Redis創(chuàng)建的子進(jìn)程內(nèi)存消耗镊讼。

8.3 內(nèi)存管理

8.3.1 控制內(nèi)存上限

maxmemory參數(shù)設(shè)置最大可用內(nèi)存,一方面方式所用內(nèi)存超過(guò)物理內(nèi)存平夜,另一方面用于緩存蝶棋,超過(guò)上限時(shí),觸發(fā)內(nèi)存回收忽妒,以便釋放空間玩裙。

8.3.2 內(nèi)存回收

a.刪除過(guò)期key對(duì)象
參考上文
b.內(nèi)存溢出控制策略
內(nèi)存達(dá)到maxmemory上限時(shí)會(huì)觸發(fā)溢出控制策略,Redis提供了6種溢出控制策略:
1)noeviction:即拒絕寫(xiě)只響應(yīng)讀(默認(rèn))段直;
2) volatile-lru:根據(jù)LRU(Least Recently Used吃溅,即最近最少使用),刪除過(guò)期的key數(shù)據(jù)鸯檬,以騰出空間决侈。如果沒(méi)有可刪除的過(guò)期key數(shù)據(jù),回退到noeviction喧务;
3)allkeys-lru:不考慮過(guò)期key數(shù)據(jù)的LRU算法赖歌;
4)allkeys-random:隨機(jī)刪除所有鍵枉圃;
5)volatile-random:隨機(jī)刪除過(guò)期鍵;
6)volatile-ttl:根據(jù)鍵值對(duì)象的ttl屬性庐冯, 刪除將要過(guò)期的key數(shù)據(jù)孽亲。 如果沒(méi)有可刪除的數(shù)據(jù), 回退到noeviction策略展父。

8.3.3 手動(dòng)內(nèi)存回收

可以使用scan+object idletime命令批量查詢(xún)哪些key長(zhǎng)時(shí)間未被訪(fǎng)問(wèn)返劲, 然后對(duì)key進(jìn)行清理, 可降低內(nèi)存占用栖茉。

8.4 內(nèi)存優(yōu)化

8.4.1 縮減key(key值越短越好)和value的值的大小

主要方法有把key和value序列化成二進(jìn)制數(shù)組數(shù)據(jù)(可以通過(guò)protostuff,kryo等高效序列化工具)或者序列化成json篮绿,xml壓縮(如GZIP,Snappy壓縮工具)再放入Redis衡载,去掉value中不必要的字段搔耕。

8.4.2 共享對(duì)象池

共享對(duì)象池是指Redis內(nèi)部維護(hù)[0-9999]的整數(shù)對(duì)象池隙袁,避免Redis封裝RedisObject(Redis存儲(chǔ)的數(shù)據(jù)都使用redisObject來(lái)封裝)內(nèi)存消耗痰娱。

8.4.3 字符串優(yōu)化

a.Redis針對(duì)字符串采用了預(yù)分配機(jī)制,防止更新操作需要不斷重分配內(nèi)存和字節(jié)數(shù)據(jù)拷貝嘱吗,但是同事造成了內(nèi)存浪費(fèi)苗踪。因此盡量減少字符串頻繁修改操作(如append盛嘿、setrange),直接使用set修改字符串坡贺。

8.4.4 字符串重構(gòu)

避免每份數(shù)據(jù)作為字符串整體存儲(chǔ), 像json這樣的數(shù)據(jù)可以使用hash結(jié)構(gòu)箱舞, 使用二級(jí)結(jié)構(gòu)存儲(chǔ)能夠節(jié)省內(nèi)存遍坟。 可以使用hmget、 hmset命令支持字段的部分讀取修改晴股, 而不用每次整體存取愿伴。

8.4.5 編碼優(yōu)化

Redis通過(guò)不同編碼實(shí)現(xiàn)效率和空間的平衡,編碼類(lèi)型轉(zhuǎn)換在Redis寫(xiě)入數(shù)據(jù)時(shí)自動(dòng)完成电湘, 這個(gè)轉(zhuǎn)換過(guò)程是不可逆的隔节, 轉(zhuǎn)換規(guī)則只能從小內(nèi)存編碼向大內(nèi)存編碼轉(zhuǎn)換。針對(duì)性能要求較高的場(chǎng)景使用ziplist

8.4.6 控制key的數(shù)量

利用Redis的數(shù)據(jù)結(jié)構(gòu)降低外層鍵的數(shù)量寂呛,比如把大量鍵分組映射到多個(gè)hash(用field記錄副key)結(jié)構(gòu)中降低鍵的數(shù)量怎诫。

9.Redis的經(jīng)典問(wèn)題

9.1 緩存穿透

緩存穿透:指查詢(xún)的數(shù)據(jù),在數(shù)據(jù)庫(kù)不存在的情況贷痪。這樣程序會(huì)先訪(fǎng)問(wèn)Redis幻妓,沒(méi)有找到數(shù)據(jù),再查詢(xún)數(shù)據(jù)庫(kù)劫拢,也沒(méi)有找到肉津,然后返回空胖缤。如果惡意大量的訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)中不存在的數(shù)據(jù),造成資源浪費(fèi)阀圾,對(duì)數(shù)據(jù)庫(kù)造成壓力哪廓,甚至?xí)?dǎo)致業(yè)務(wù)不可用,甚至壓垮數(shù)據(jù)庫(kù)初烘。
解決方法:空值緩存涡真,即如果數(shù)據(jù)庫(kù)查詢(xún)的時(shí)候,沒(méi)有查詢(xún)到數(shù)據(jù)肾筐,也將空值緩存到Redis中哆料,并設(shè)置過(guò)期時(shí)間(相對(duì)普通的數(shù)據(jù),過(guò)期時(shí)間設(shè)置較短吗铐,避免占用過(guò)大的存儲(chǔ)空間)东亦,這樣就避免造成對(duì)數(shù)據(jù)庫(kù)的壓力。

9.2 緩存雪崩

緩存雪崩: 指在某一時(shí)間段內(nèi)唬渗,緩存數(shù)據(jù)集中過(guò)期失效典阵。比如CRM系統(tǒng),工作日早上9點(diǎn)左右镊逝,會(huì)迎來(lái)大量的數(shù)據(jù)訪(fǎng)問(wèn)壮啊,加入緩存是2個(gè)小時(shí),那么到了11點(diǎn)撑蒜,很多緩存數(shù)據(jù)就會(huì)過(guò)期歹啼,很多數(shù)據(jù)的訪(fǎng)問(wèn)都會(huì)落到數(shù)據(jù)庫(kù)上,導(dǎo)致此時(shí)的數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)量大增座菠,會(huì)對(duì)數(shù)據(jù)庫(kù)產(chǎn)生周期性的壓力狸眼。另外,Redis集群的某些節(jié)點(diǎn)宕機(jī)浴滴,也會(huì)導(dǎo)致緩存雪崩拓萌,而且是不可預(yù)知的,此時(shí)DB的訪(fǎng)問(wèn)量大增巡莹,甚至壓垮DB司志。
解決方法: 針對(duì)不同的類(lèi)型數(shù)據(jù),如客戶(hù)降宅,銷(xiāo)售訂單等骂远,采用不同的緩存過(guò)期時(shí)間;在同一個(gè)對(duì)象類(lèi)中腰根,采取隨機(jī)因子的方式激才,設(shè)置不同的過(guò)期時(shí)間。

9.3 緩存擊穿

緩存擊穿: 指一個(gè)cache中的key并發(fā)訪(fǎng)問(wèn)量非常大,形成了熱點(diǎn)瘸恼。當(dāng)這個(gè)key過(guò)期后劣挫,持續(xù)的大并發(fā)就會(huì)跳過(guò)緩存,直接訪(fǎng)問(wèn)DB东帅,造成DB的壓力压固。
解決方法: 對(duì)熱點(diǎn)的key程序?qū)崿F(xiàn)自動(dòng)轉(zhuǎn)為永不過(guò)期緩存,可以在value中設(shè)置超時(shí)時(shí)間靠闭,程序內(nèi)部校驗(yàn)是否過(guò)期帐我。如果過(guò)期,異步發(fā)起一個(gè)線(xiàn)程更新緩存(可以使用鎖控制并發(fā))愧膀。

10.哨兵(Sentinel)

哨兵是Redis的高可用解決方案拦键,它由一個(gè)或者多個(gè)Sentinel實(shí)例組成Sentinel系統(tǒng),Sentinel系統(tǒng)監(jiān)視n個(gè)服務(wù)器以及對(duì)應(yīng)的從服務(wù)器檩淋,當(dāng)監(jiān)視到主服務(wù)器線(xiàn)下時(shí)芬为,自動(dòng)將主服務(wù)器對(duì)應(yīng)的某個(gè)從服務(wù)器升級(jí)為新的主服務(wù)器,代替主服務(wù)器繼續(xù)處理請(qǐng)求蟀悦。
哨兵系統(tǒng)架構(gòu):


Sentinel .jpg

10.1 定時(shí)監(jiān)控

a.每個(gè)哨兵實(shí)例會(huì)間隔10秒向主節(jié)點(diǎn)和從節(jié)點(diǎn)發(fā)送info命令媚朦,獲取最新的Redis的拓?fù)浣Y(jié)構(gòu);
b.每隔2秒每個(gè)哨兵節(jié)點(diǎn)會(huì)向Redis數(shù)據(jù)節(jié)點(diǎn)的指定頻道上發(fā)送 該節(jié)點(diǎn)對(duì)主節(jié)點(diǎn)的判斷以及自身的信息熬芜,同時(shí)也從該頻道上獲取其他哨兵節(jié)點(diǎn)的發(fā)送的信息莲镣;
c.每隔1秒,每個(gè)哨兵節(jié)點(diǎn)會(huì)向主節(jié)點(diǎn)涎拉、從節(jié)點(diǎn)、其他哨兵節(jié)點(diǎn)發(fā)送ping命令的圆,做心跳檢測(cè)鼓拧,來(lái)判斷節(jié)點(diǎn)的可達(dá)性。

10.2 節(jié)點(diǎn)下線(xiàn)

主觀(guān)下線(xiàn)
如果哨兵節(jié)點(diǎn)發(fā)送心跳檢測(cè)越妈,超過(guò)down_after_milliseconds后季俩,仍然沒(méi)有得到回復(fù),哨兵節(jié)點(diǎn)會(huì)認(rèn)為該節(jié)點(diǎn)為失效節(jié)點(diǎn)梅掠,稱(chēng)為主觀(guān)下線(xiàn)酌住。
客觀(guān)下線(xiàn)
當(dāng)一個(gè)哨兵節(jié)點(diǎn)主觀(guān)下線(xiàn)的節(jié)點(diǎn)是Master時(shí),該哨兵節(jié)點(diǎn)會(huì)向其他哨兵節(jié)點(diǎn)詢(xún)問(wèn)當(dāng)前Master節(jié)點(diǎn)的狀態(tài)阎抒,當(dāng)超過(guò)quorum指定個(gè)數(shù)時(shí)酪我,該哨兵節(jié)點(diǎn)認(rèn)為Master節(jié)點(diǎn)確實(shí)失效,然后做出客觀(guān)下線(xiàn)且叁。

10.3 選舉哨兵主節(jié)點(diǎn)

故障轉(zhuǎn)移有一個(gè)哨兵節(jié)點(diǎn)負(fù)責(zé)都哭,當(dāng)哨兵系統(tǒng)有多個(gè)哨兵節(jié)點(diǎn)時(shí),需要進(jìn)行哨兵領(lǐng)導(dǎo)者選舉:
當(dāng)一個(gè)哨兵節(jié)點(diǎn)最先完成客觀(guān)下線(xiàn)后,會(huì)向其他哨兵節(jié)點(diǎn)發(fā)送"我要成為領(lǐng)導(dǎo)者"欺矫,其他哨兵節(jié)點(diǎn)如果沒(méi)有同意過(guò)其他哨兵節(jié)點(diǎn)的請(qǐng)求纱新,則返回同意該請(qǐng)求,否則拒絕穆趴。當(dāng)該節(jié)點(diǎn)獲得的同意票大于等于max(quorum脸爱,num(sentinels)/2 +1)時(shí),該哨兵節(jié)點(diǎn)成為領(lǐng)導(dǎo)者未妹;否則進(jìn)入下一輪選舉阅羹。實(shí)際選舉過(guò)程非常快教寂,基本誰(shuí)先完成客觀(guān)下線(xiàn)捏鱼,誰(shuí)就是領(lǐng)導(dǎo)者。

10.4 故障轉(zhuǎn)移

哨兵領(lǐng)導(dǎo)者負(fù)責(zé)故障轉(zhuǎn)移酪耕,具體過(guò)程如下:
1.選取slave節(jié)點(diǎn)作為Master節(jié)點(diǎn)导梆;
?1.1 過(guò)濾失效的slave節(jié)點(diǎn);
?1.2 如果有slave優(yōu)先級(jí)迂烁,則根據(jù)slave節(jié)點(diǎn)的優(yōu)先級(jí)返回slave節(jié)點(diǎn)看尼,否則繼續(xù);
?1.3 選擇復(fù)制偏移量最大(這樣節(jié)省復(fù)制時(shí)間)的slave節(jié)點(diǎn)盟步,如果沒(méi)有則繼續(xù)藏斩;
?1.4 選擇runid最小的slave節(jié)點(diǎn);
2.哨兵領(lǐng)導(dǎo)者根據(jù)第一步選舉出的slave節(jié)點(diǎn)執(zhí)行slaveof on one命令却盘,使其成為Master;
3.哨兵領(lǐng)導(dǎo)者向剩余的slave節(jié)點(diǎn)發(fā)送命令狰域,讓他們成為新Master節(jié)點(diǎn)的從節(jié)點(diǎn);
4.哨兵節(jié)點(diǎn)集合將舊的Master節(jié)點(diǎn)更改為Slave節(jié)點(diǎn)黄橘,并持續(xù)監(jiān)控兆览,當(dāng)其恢復(fù)可用后,讓它c(diǎn)opy新的Master節(jié)點(diǎn)塞关。

當(dāng)前很少用哨兵抬探,目前推薦使用Redis集群,這個(gè)的話(huà)就避免了單個(gè)主節(jié)點(diǎn)的問(wèn)題帆赢。

11.Redis跳表

可參考文章小压,寫(xiě)的很詳細(xì)。
https://www.cnblogs.com/Elliott-Su-Faith-change-our-life/p/7545940.html

跳表的優(yōu)點(diǎn):

1. 存儲(chǔ)空間小
改變關(guān)于節(jié)點(diǎn)具有給定級(jí)別的概率的參數(shù)將使其比b樹(shù)更少的內(nèi)存占用
2. 范圍查找
紅黑樹(shù)結(jié)構(gòu)在范圍查找時(shí)椰于,要進(jìn)行中序遍歷怠益;
跳表查詢(xún)范圍非常簡(jiǎn)單,只需要在找到小值之后廉羔,對(duì)第1層鏈表進(jìn)行若干步的遍歷就可以實(shí)現(xiàn)溉痢。
3. 插入刪除效率
紅黑樹(shù)的插入和刪除會(huì)導(dǎo)致樹(shù)的旋轉(zhuǎn)僻造,跳表的插入和刪除只需要修改相鄰節(jié)點(diǎn)的指針,簡(jiǎn)單又快速孩饼。

12.Redis的字典

Redis數(shù)據(jù)庫(kù)使用字典作為底層的實(shí)現(xiàn)髓削,利用字典提供CRUD操作。

12.1 Redis的Hash表

Redis使用的Hash表dict.h/dictht镀娶,具體格式如下:

typedef struct dictht {

    dictEntry **table;  // Hash表數(shù)組
    unsigned long size;// Hash表的大小
    unsigned long sizemasky;// Hash表的大小的掩碼立膛,大小總是等于 size-1,用于計(jì)算索引值
    unsigned long size;  // table中已有節(jié)點(diǎn)的數(shù)量

    
} dictht梯码;

typedef struct dictEntry {
      // key
     void *key;

     // value宝泵,可以是指針*val、整數(shù)u64和整數(shù)s64的其中之一
     union {
       void *val;
       uint64_t u64;
       int64_t s64;
       } v;

struct dictEntry *next;  // 指向下一個(gè)Hash節(jié)點(diǎn)轩娶,形成鏈表儿奶,解決Hash沖突

}   dictEntry;


數(shù)據(jù)保存格式


Redis字典的Hash表存儲(chǔ)結(jié)構(gòu).jpg

12.2 Redis字典

Redis的字典由dict.h/dict 結(jié)構(gòu):

typedef struct dict {
     // 類(lèi)型特定函數(shù),指向一簇用于操作特定類(lèi)型key-value的函數(shù)鳄抒,主要函數(shù)有hash值計(jì)算(HashFunction)闯捎、復(fù)制key(keyDup)、復(fù)制value(valDup)许溅、對(duì)比key大小(keyCompare)瓤鼻、刪除key(keyDestructor)和刪除value(valDestructor)的函數(shù)等。
      dictType *type;
      // 私有數(shù)據(jù)贤重,保存了傳給dictType的參數(shù)
      void *private;
      // Hash表茬祷,字典只使用ht[0],ht[1] 只會(huì)在對(duì)ht[0]進(jìn)行rehash時(shí)使用
      dictht ht[2];
      // rehash索引并蝗,記錄當(dāng)前rehash的進(jìn)度祭犯,當(dāng)rehash不在進(jìn)行中,值為-1
      in trehashidx;

} dict;

沒(méi)有進(jìn)行rehash的字典數(shù)據(jù)結(jié)構(gòu):


沒(méi)有進(jìn)行rehash的字典數(shù)據(jù)結(jié)構(gòu).jpg

Hash值的計(jì)算:
hash = dict - > type -> hashFunction(key);
Redis使用MurmurHash2作為其Hash函數(shù)借卧。

index的計(jì)算:
index = hash & dict - > ht[i].sizemask;
//字典一般只使用ht[0]盹憎,ht[1] 只會(huì)在對(duì)ht[0]進(jìn)行rehash時(shí)使用

沖突解決:
Redis使用鏈表方法來(lái)解決hash值的沖突,很類(lèi)似JDK1.7的HashMap實(shí)現(xiàn)铐刘。

12.3 Redis的ReHash

針對(duì)Hash表保存的key-value的增加和減少,Redis提供了對(duì)hash表進(jìn)行擴(kuò)展或者收縮的功能影晓,以讓hash表的負(fù)載因子維持在一個(gè)合理的范圍之內(nèi)镰吵。
具體步驟如下:
1.為ht[1] Hash表分配空間:
如果是擴(kuò)展(負(fù)載因子大于1或者負(fù)載因子大于5(如果正在執(zhí)行BGSAVE或者BGREWRIGEAOF時(shí))),ht[1]的大小為第一個(gè)大于等于ht[0].used * 2的2^n;
如果是收縮(負(fù)載因子小于0.1時(shí))挂签,ht[1]的大小為第一個(gè)大于等于ht[0].used 的 2的2^n.
2.將ht[0]中的所有key-value 重新散列到ht[1] 上疤祭,需要重新計(jì)算hash值和索引值,因?yàn)閔t[1]的長(zhǎng)度和ht[0]不一樣饵婆;
3.遷移完成后勺馆,釋放ht[0]的空間,交換ht[0]和ht[1],為下一次rehash準(zhǔn)備草穆。

負(fù)載因子load_facotr = ht[0].used / ht[0].size;

13.Redis Cluster

Redis 3.0之后灌灾,Redis推出了Redis集群,解決了單點(diǎn)的問(wèn)題悲柱,每個(gè)節(jié)點(diǎn)之間是去中心化的锋喜,并且提供了sharding(數(shù)據(jù)分片)、replication(主從復(fù)制)豌鸡、failover(故障轉(zhuǎn)移)等解決方案嘿般。Redis Cluster的最小配置節(jié)點(diǎn)個(gè)數(shù)為6個(gè)(即3主3從,主節(jié)點(diǎn)提供讀寫(xiě)涯冠,從節(jié)點(diǎn)不提供請(qǐng)求服務(wù))炉奴,最大16384個(gè)節(jié)點(diǎn),采用虛擬槽分區(qū)的原理蛇更,即根據(jù)key的Hash值映射到0~16383個(gè)整數(shù)槽內(nèi)瞻赶。另外,Redis集群具有感知主備的能力械荷。

注意Redis Cluster和哨兵的卻別共耍,而且并不是功能重疊,也不是相互替換的吨瞎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末痹兜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子颤诀,更是在濱河造成了極大的恐慌字旭,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崖叫,死亡現(xiàn)場(chǎng)離奇詭異遗淳,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)心傀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)屈暗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人脂男,你說(shuō)我怎么就攤上這事养叛。” “怎么了宰翅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵弃甥,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我汁讼,道長(zhǎng)淆攻,這世上最難降的妖魔是什么阔墩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮瓶珊,結(jié)果婚禮上啸箫,老公的妹妹穿的比我還像新娘。我一直安慰自己艰毒,他們只是感情好筐高,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著丑瞧,像睡著了一般柑土。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绊汹,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天稽屏,我揣著相機(jī)與錄音,去河邊找鬼西乖。 笑死狐榔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的获雕。 我是一名探鬼主播薄腻,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼届案!你這毒婦竟也來(lái)了庵楷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤楣颠,失蹤者是張志新(化名)和其女友劉穎尽纽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體童漩,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弄贿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矫膨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片差凹。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖侧馅,靈堂內(nèi)的尸體忽然破棺而出直奋,到底是詐尸還是另有隱情,我是刑警寧澤施禾,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站搁胆,受9級(jí)特大地震影響弥搞,放射性物質(zhì)發(fā)生泄漏邮绿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一攀例、第九天 我趴在偏房一處隱蔽的房頂上張望船逮。 院中可真熱鬧,春花似錦粤铭、人聲如沸挖胃。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)酱鸭。三九已至,卻和暖如春垛吗,著一層夾襖步出監(jiān)牢的瞬間凹髓,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工怯屉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蔚舀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓锨络,卻偏偏與公主長(zhǎng)得像赌躺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子羡儿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345