這是我參與更文挑戰的第25天,活動詳情查看: 更文挑戰 redis爲啥單線程還能作到高響應?java
redis
和數據相比除了他們的結構型顛覆之外!還有他們存儲位置也是不相同。傳統數據庫將數據存儲在硬盤上每次數據操做都須要IO
而Redis
是將數據存儲在內存上的。這裏稍微解釋下IO是啥意思。IO就是輸入流輸出流方式將數據在硬盤和內存之間進行交互!而redis
直接在內存上就剩下了IO
操做。這也是redis
快的緣由之一吧程序員
redis
又將數據存儲在內存上那麼redis
確定不能肆無忌憚的進行存儲 。這就須要redis
和開發者們做出相應的優化redis
在配置文件(redis.conf
)中經過maxmemory
參數指定redis
設置整個對內存的支配大小!redis
中填值的時候根據本身的需求設置相應的key過時時間。這樣沒必要要的數據就會被redis
過時驅逐策略清楚。從而節省內存供別人使用本文凌駕於redis
基礎之上,這裏筆者默認你們都已經安裝了redis
. 並實際使用過redis
redis
maxmemory
指定大小。在redis.conf
配置文件中能夠直接指定。他的單位時byte。redis
經過config
命令來設置redis.conf
中的配置。redis
自己角度出發設置了內存限制,這樣不用擔憂他們吞噬系統內存!下面就須要咱們開發者設計角度約束本身了。expire key time
複製代碼
設置過時時間默認單位時S 。數據庫
而後經過ttl 命令能夠查看剩餘過時時間小程序
ttl
可以觀察到剩餘時間在不斷的減小!當減小到0的時候就被給驅逐策略驅逐!注意這裏說的是驅逐策略驅逐並非意味着立馬被刪除del key
直接將key刪除了那麼該key對應的過時天然也就不存在了!這種狀況筆者也算做是更新過時時間set
getset
等命令從新設置key、value方式會覆蓋過時時間 , 直接被覆蓋成-1set
、 getset
包括del
嚴格意義是覆蓋過時時間。真正作到更新過時時間的仍是expire .在expire是已最新爲準的!append
、incr
等命令是改變值這種命令是不會影響到原來的過時設置的redis
最大內存設置爲1MB
, 設置大小隨便最好能小點。而後咱們經過Java
小程序不斷向redis
中填充。最終當內存不夠使用時就會報錯redis
拒絕了!不只僅是新增的被拒絕,就算此時咱們想改變已經在redis
中的key的值也是不可用的public static void main(String[] args) {
Jedis jedis = new Jedis("39.102.60.114", 6379);
jedis.auth("Qq025025");
Integer index = 1;
while (true) {
String uuid = UUID.randomUUID().toString();
jedis.set(index.toString(), uuid);
System.out.println(index++);
}
}
複製代碼
無論是append 仍是set 都是報OOM command not allowed when used memory > maxmemory
。代碼中打印和redis
鍵個數一致;說明咱們默認的淘汰策略是直接拒絕緩存
總結下來就是:當redis
內存被使用滿了後,任何的寫操做都會被拒絕!markdown
當沒有足夠內存時難道就這麼直接拒絕嗎?上面也提到了須要咱們程序員本身根據需求設置鍵過時已釋放內存供其餘有須要的key使用!那麼設置了過時key以後這些key又是怎麼被清除的呢? 這就牽扯出咱們的淘汰策略併發
當內存告警時
redis
會將近期不多使用且設置了過時時間的key剔除出去,即便該key尚未到過時時間。若是沒有符合的key也就是執行以後內存仍然不足時將會和默認淘汰策略noeviction
拋出同樣的錯誤OOM command not allowed when used memory > maxmemory
app
redis.conf
中配置咱們最近不多使用策略. maxmemory-policy volatile-lru
。 而後重啓咱們的redis
服務 。重啓以後flushall
清空全部數據,咱們在經過上面的Java
程序從新生成下數據!public static void main(String[] args) {
Jedis jedis = new Jedis("39.102.60.114", 6379);
jedis.auth("Qq025025");
Integer index = 1;
while (true) {
String uuid = UUID.randomUUID().toString();
if (index < 100) {
jedis.setex(index.toString(),360, uuid);
} else {
jedis.set(index.toString(), uuid);
}
System.out.println(index++);
}
}
複製代碼
redis
庫中的key數量!就是由於咱們爲前100設置了過時時間。當內存不足時redis
就會將當前設置了過時時間的key中最近最少使用的key進行剔除!因此咱們計數器會大於鍵數量。由於有部分鍵被清除了!咱們獲取前100的key都是null , 說明被刪除了! 那麼爲何本次計數器不是比上次多100 。 那是由於咱們每次存儲進來的是uuid, 所佔長度都不是固定的。還有自己淘汰策略也是佔用內存的noeviction:拒絕寫請求,正常提供讀請求,這樣能夠保證已有數據不會丟失(默認策略);
2. volatile-lru:嘗試淘汰設置了過時時間的key,雖少使用的key被淘汰,沒有設置過時時間的key不會淘汰;
3. volatile-ttl:跟volatile-lru幾乎同樣,可是他是使用的key的ttl值進行比較,最早淘汰ttl最小的key;
4. volatile-random:其餘同上,惟一就是他經過很隨意的方式隨機選擇淘汰key集合中的key;
5. allkeys-lru:區別於volatile-lru的地方就是淘汰目標是所有key,沒設置過時時間的key也不能倖免;
6. allkeys-random:這種方式同上,隨機的淘汰全部的key。
複製代碼
redis
。 僅僅這兩方面尚未徹底高效使用內存!淘汰策略是瀕臨內存不足時觸發。那麼當設置了過時時間的鍵真正到了過時時間而此時內存尚夠使用?這種場景是否是須要將過時鍵刪除呢?redis
是單線程,那麼在鍵過時的時候如何不影響對外服務的同時清除過時鍵呢?答案是【不行】。嚴格意義是沒法解決的由於單線程同時只能作一件事!既然沒法解決那麼咱們能夠達到一種協調狀態!若是同一時刻出現一個過時鍵那麼清除鍵很快這時候阻塞外部服務的時間很短可能毫秒級設置納秒級!redis
如何應對同一時間過多數據過時的場景,他的刪除過時鍵的方法略有不一樣!key
須要設置一個定時器進行跟蹤。redis
這裏筆者猜想應該是啓用另外線程來進行定時跟蹤!這裏有清除的還請幫忙解答下?key
不少的時候!咱們的CPU就須要不斷的執行這些定時器從而致使CPU資源緊張。最終會影響到redis
服務的性能redis
會每隔100ms
執行一次定時器,定時器的任務就是隨機抽取20個設置過時的key
。 判斷是否進行清除。上面圖示中說明中寫錯了不是10S , 而是每隔100ms
。請原諒個人粗心!!!25ms
。 也就是說對於客戶端來講服務端的延遲不會超過25ms
。redis
並不急着去清除這些數據,而是等到該key被再次請求時進行刪除!這樣在最終效果上是沒有問題的。redis
中。redis
內部是使用惰性清除和按期清除兩種方式結合使用,最終保重CPU和內存之間的一種平衡!redis
給咱們開發者提供的功能。咱們能夠根據本身的業務需求合理的設置鍵的過時時間,從而保證內存的高可用redis.conf
中咱們能夠設置 hz 10
表明1S
中平均執行幾回這也是咱們上面所說的100MS
的由來。可是僅僅按期刪除會產生遺漏數據因此咱們還須要加上惰性清除,最終保證對客戶端來講數據是準確實時清除的。redis
服務中還會堆積不少過時的無效key 。這個時候若是內存不夠用了的話那又該怎麼辦呢?這時候咱們須要設置淘汰策略好比果volatile-lru
. 就會將最近最少使用的設置過時key進行清除從而保證儘量的接收更多的有效數據!redis
常見的災難場景,咱們下章節繼續分析如何產生的、而且如何進行修復進行展開討論。