過期時(shí)間設(shè)置
在Redis中提供了Expire命令設(shè)置一個(gè)鍵的過期時(shí)間,到期以后Redis會(huì)自動(dòng)刪除它。這個(gè)在我們實(shí)際使用過程中用得非常多。
redis 過期刪除的原理
消極方法:在主鍵訪問時(shí)如果發(fā)現(xiàn)它已經(jīng)失效,那么就刪除它
積極方法:周期性地從設(shè)置了失效時(shí)間的主鍵中選擇一部分失效的主鍵刪除。
redis 發(fā)布訂閱
發(fā)布/訂閱模式包含兩種角色,分別是發(fā)布者和訂閱者。
訂閱者可以訂閱一個(gè)或多個(gè)頻道,而發(fā)布者可以向指定的頻道發(fā)送消息,所有訂閱此頻道的訂閱者都會(huì)受到該消息。
redis 的數(shù)據(jù)是如何持久化的?
RDB根據(jù)指定的規(guī)則定時(shí)將內(nèi)存中的數(shù)據(jù)存儲(chǔ)在硬盤上。
AOF在每次執(zhí)行命令后將命令本身記錄下來。
可以單獨(dú)/結(jié)合使用。
RDB
當(dāng)符合一定條件,redis 會(huì)單獨(dú)創(chuàng)建 fork 一個(gè)子進(jìn)程來進(jìn)行持久化,會(huì)先將數(shù)據(jù)寫入一個(gè)臨時(shí)文件 dump.rdb,等到持久化過程都結(jié)束了,
再用這個(gè)臨時(shí)文件替換上次持久化好的文件。整個(gè)過程中,主進(jìn)程是不進(jìn)行任何IO操作的。這就確保了極高的性能。
如果需要進(jìn)行大規(guī)模的數(shù)據(jù)的恢復(fù),且對(duì)于數(shù)據(jù)恢復(fù)的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
RDB唯一的缺點(diǎn)是最后一次持久化后的數(shù)據(jù)可能丟失。
fork的作用是復(fù)制一個(gè)與當(dāng)前線程一樣的進(jìn)程。新進(jìn)程的所有數(shù)據(jù)(變量、環(huán)境變量、程序計(jì)數(shù)器)數(shù)值都和原進(jìn)程一致,
但是是一個(gè)全新的進(jìn)程,并作為原進(jìn)程的子進(jìn)程。
符合條件
- 配置規(guī)則
save seconds changes - save :當(dāng)執(zhí)行save命令時(shí),Redis同步做快照操作,在快照?qǐng)?zhí)行過程中會(huì)阻塞所有來自客戶端的請(qǐng)求。
當(dāng)redis內(nèi)存中的數(shù)據(jù)較多時(shí),通過該命令將導(dǎo)致Redis較長(zhǎng)時(shí)間的不響應(yīng)。所以不建議在生產(chǎn)環(huán)境上使用這個(gè)命令,而是推薦使用bgsave命令 - bgsave :bgsave 命令可以在后臺(tái)異步地進(jìn)行快照操作。快照的同時(shí)服務(wù)器還可以繼續(xù)響應(yīng)來自客戶端的請(qǐng)求。
執(zhí)行BGSAVE后,Redis會(huì)立即返回ok表示開始執(zhí)行快照操作。
當(dāng)redis 內(nèi)存中的數(shù)據(jù)較多時(shí),通過該命令導(dǎo)致喏、較長(zhǎng)時(shí)間的
- save :當(dāng)執(zhí)行save命令時(shí),Redis同步做快照操作,在快照?qǐng)?zhí)行過程中會(huì)阻塞所有來自客戶端的請(qǐng)求。
- flushall 清空
- 執(zhí)行復(fù)制操作
AOF 方式 append-only-file
當(dāng)使用redis 存儲(chǔ)非臨時(shí)數(shù)據(jù)時(shí),一般需要打開AOF持久化來降低進(jìn)程終止導(dǎo)致的數(shù)據(jù)丟失。
AOF可以將redis執(zhí)行的每一條命令追加到硬盤文件中。這一過程會(huì)降低redis 的性能。
但大部分情況下這個(gè)影響是能夠接受的。另外使用較快的硬盤可以調(diào)AOF性能。
- auto-aof-rewrite-perecntage 100 當(dāng)前寫入日志文件的大小超過上一次rewrite之后的文件大小的百分之100時(shí)就是2倍時(shí)觸發(fā)rewrite。
- auto-aof-rewrite-min-size 64mb 最小重寫大小
redis 內(nèi)存回收策略 maxmemory-policy
redis提供了多種內(nèi)存回收策略,當(dāng)內(nèi)存容量不足時(shí),為了保證程序的運(yùn)行,不得不淘汰內(nèi)存中的一些對(duì)象,釋放這些對(duì)象占用空間。
淘汰對(duì)象選擇? 已設(shè)置過期時(shí)間的數(shù)據(jù)集(server.db[i].expires)
- 默認(rèn)的策略是 noeviction。當(dāng)內(nèi)存使用達(dá)到閾值時(shí),所以引起申請(qǐng)內(nèi)存的命令都會(huì)報(bào)錯(cuò)。
- allkeys-lur:從數(shù)據(jù)集中挑選最近最少使用的數(shù)據(jù)淘汰。 應(yīng)用對(duì)緩存的訪問都是相對(duì)熱點(diǎn)數(shù)據(jù)。
- volatile-random:從已設(shè)置時(shí)間的數(shù)據(jù)集中任意選擇數(shù)據(jù)淘汰。
- volatile-lur:從已設(shè)置時(shí)間的數(shù)據(jù)集中選擇最近最少數(shù)據(jù)淘汰。
- volatile-ttl:從已設(shè)置時(shí)間的數(shù)據(jù)集中挑選將要過期的數(shù)據(jù)淘汰。
redis 是單進(jìn)程單線程 來處理來自所有客戶端的并發(fā)請(qǐng)求。
cpu 并不是redis的瓶頸所在,redis的瓶頸主要機(jī)器的內(nèi)存和帶寬。
redis能處理高并發(fā)請(qǐng)求,是多路復(fù)用的并發(fā)IO(一個(gè)計(jì)算單元來處理來自多個(gè)客戶端的請(qǐng)求)
多路復(fù)用
redis是跑在單線程的,所有操作都是按照順序線性執(zhí)行的,但是由于讀寫操作等待用戶輸入或者輸出都是阻塞的,所以IO操作在一般情況下往往不能直接返回,
這會(huì)導(dǎo)致某一文件的IO阻塞導(dǎo)致整個(gè)進(jìn)程無法對(duì)其他客戶提供服務(wù),而IO多路復(fù)用就是為了這個(gè)問題出現(xiàn)的。
- 同步阻塞
- 同步非阻塞 socket被設(shè)置為NONBLOCK
- IO多路復(fù)用:reactor設(shè)計(jì)模式,異步阻塞IO。java中的selector和linux中的epol都是這種模型
- 異步IO:經(jīng)典的proactor設(shè)計(jì)迷失,也稱為異步非阻塞IO
在reids中使用 LUA 腳本
原子性問題:
redis雖然是單一線程的,但是仍然會(huì)存在線程安全問題。不是來自于redis的服務(wù)器內(nèi)部。
而是redis作為數(shù)據(jù)服務(wù)器,是提供給多個(gè)客戶端使用的。多個(gè)客戶端的操作就相當(dāng)于同一個(gè)進(jìn)程下的多個(gè)線程。
如果多個(gè)客戶端之間沒有做好數(shù)據(jù)的同步策略,就會(huì)產(chǎn)生數(shù)據(jù)不一致的情況。
效率
基于內(nèi)存的數(shù)據(jù)庫
LUA 腳本
- 減少網(wǎng)絡(luò)開銷,在lua腳本中可以把多個(gè)命令放在同一個(gè)腳本中運(yùn)行
- 原子操作:redis回想整個(gè)腳本作為一個(gè)命令執(zhí)行,中間不會(huì)被其他命令插入。腳本執(zhí)行過程中無需擔(dān)心會(huì)出現(xiàn)競(jìng)態(tài)條件。
- 復(fù)用性:客戶端發(fā)送的腳本會(huì)永遠(yuǎn)存儲(chǔ)在redis中。復(fù)用。
local key = "ratelimit:"..KEYS[1]
local limit = tonumber(ARGV[1])
local expireTime = ARGV[2]
local times = redis.call('incr',key)
if times == 1 then
redis.call('expire',key,expireTime)
end
if times > limit then
return 0
end
return 1
evalsha命令
- script load "return redis.call('set','lua','00')" 將腳本加入緩存。并未執(zhí)行命令
- 執(zhí)行evalsha命令 evalsha "id"