1巩那、什么是Redis
Redis 是一個基于內(nèi)存的高性能 key-value數(shù)據(jù)庫。支持多種數(shù)據(jù)類型
2此蜈、簡單描述Redis的特點
Redis本質(zhì)上是一個key-value類型的內(nèi)存數(shù)據(jù)庫即横,很像memcached,整個數(shù)據(jù)庫統(tǒng)統(tǒng)加載在內(nèi)存當中進行操作裆赵,定期通過異步操作把數(shù)據(jù)庫數(shù)據(jù)(內(nèi)存中)flush到硬盤上進行保存东囚。
純內(nèi)存操作,Redis的性能非常出色战授,每秒可以處理超過10萬次讀寫操作页藻,是已知性能最快的key-value DB
Redis的出色之處,不僅僅是性能植兰,Redis最大的魅力是支持保存多種數(shù)據(jù)結(jié)構(gòu)份帐;
此外,當個value的最大限制是1GB楣导,不像memcached只能保存1MB的數(shù)據(jù)废境,因此Redis可以用來實現(xiàn)很多有用的功能则披;
Redis的主要缺點挨约,就是數(shù)據(jù)庫容量受到物理內(nèi)容的限制,不能用做海量數(shù)據(jù)的高性能讀寫童太,因此Redis適合的場景主要局限在較小數(shù)據(jù)量的高性能操作和運算上
3膝晾、Redis支持的數(shù)據(jù)類型
String 栓始、List 、 Set血当、 Sorted Set幻赚、 Hash
4禀忆、為什么Redis需要把所有數(shù)據(jù)放到內(nèi)存中
- 追求最快的數(shù)據(jù)讀取速度,如果直接磁盤讀取會非常慢
- 為了保證數(shù)據(jù)安全落恼,也會異步方式將數(shù)據(jù)寫入磁盤
- 可以設(shè)置Redis最大使用的內(nèi)存箩退,若達到內(nèi)存限制后將不能繼續(xù)存取數(shù)據(jù)
5、Redis是單線程的么佳谦?Redis為什么這么快戴涝,尤其是其采用單線程??
單線程
Redis是單線程處理網(wǎng)絡(luò)指令請求,所以不需要考慮并發(fā)安全問題钻蔑。
所有的網(wǎng)絡(luò)請求都是一個線程處理啥刻,但不代表所有模塊都是單線程。
高性能
因為它的所有的數(shù)據(jù)都在內(nèi)存中咪笑,所有的運算都是內(nèi)存級別的運算可帽,而且單線程避免了多線程的切換性能損耗問題;
而且正因為Redis是單線程窗怒,所以要小心使用Redis指令映跟,對于那些耗時的指令(比如keys),一定要謹慎使用,一步小心就可能會導(dǎo)致Redis卡頓扬虚;
Redis單線程處理多個并發(fā)客戶端連接:IO多路復(fù)用
Redis的IO多路復(fù)用:redis利用epoll來實現(xiàn)IO多路復(fù)用努隙,將連接信息和事件放到隊列中,依次放到文件事件分派器辜昵,事件分派器將事件分發(fā)給事件處理器荸镊。
Nginx也是采用IO多路復(fù)用原理解決C10k問題
6、Redis的持久化機制有哪些路鹰?區(qū)別是什么?優(yōu)缺點是什么收厨?
1.RDB持久化:原理是將Redis在內(nèi)存中的數(shù)據(jù)庫記錄定時dump到磁盤上的RDB持久化
2.AOF(append only file)持久化:原理是將Redis的操作日志以追加的方式寫入文件
區(qū)別:
RDB持久化的指在指定的時間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤晋柱,實際操作過程是fork一個子進程,先將數(shù)據(jù)集寫入臨時文件诵叁,寫入成功后雁竞,再替換之前的文件,用二進制壓縮存儲
AOF持久化以日志的形式記錄服務(wù)器所處理的每一個寫拧额、刪除操作碑诉,查詢操作不會記錄,以文本的方式記錄侥锦,可以打開文件看到詳細的操作記錄
RDB 優(yōu)點:
- RDB是緊湊的二進制文件进栽,比較合適備份,全量復(fù)制等場景
- RDB恢復(fù)數(shù)據(jù)遠快于AOF
RDB缺點:
- RDB無法實現(xiàn)實時或者秒級持久化
- 新老版本無法兼容RDB格式
AOF優(yōu)點:
- 可以更好地保護數(shù)據(jù)不丟失
- appen-only模式讀取性能比較高
- 適合做災(zāi)難性的誤刪除緊急恢復(fù)
AOF缺點:
- 對于同一份文件恭垦,AOF文件要比RDB快照大
- AOF開啟后快毛,寫的QPS會有所影響格嗅,相對于RDB來說,寫QPS要下降
- 數(shù)據(jù)庫恢復(fù)比較慢唠帝,不適合做冷備
7屯掖、Redis的緩存失效策略有哪幾種
- 定時刪除策略
在設(shè)置key的過期時間的同時,為該key創(chuàng)建一個定時器襟衰,讓定時器在可以的過期時間來臨時贴铜,對可以進行刪除
優(yōu)點:保證內(nèi)存盡快釋放
缺點:如果key過多,刪除這些key會占用很多CPU時間瀑晒,而且每個key創(chuàng)建一個定時器绍坝,驗證影響性能
-
惰性刪除策略
key過期的時候不刪除,每次從數(shù)據(jù)庫獲取key的時候去檢查是否過期瑰妄,若過期陷嘴,則刪除,返回null優(yōu)點:CPU占用時間比較少
缺點:如果key很長時間沒有被獲取间坐,將不會被刪除灾挨,可能造成內(nèi)存泄漏 -
定期刪除策略
每隔一段時間執(zhí)行一次刪除(在redis.conf配置文件設(shè)置hz,1s刷新的頻率)過期key的操作優(yōu)點:可以控制刪除操作的時長和頻率竹宋,來減少CPU時間占用劳澄,可以避免惰性刪除時候內(nèi)存泄漏的問題
缺點:對內(nèi)存友好方面,不如定時策略蜈七;對cpu友好方面秒拔,不如惰性策略
Redis一般采用惰性策略+定期策略兩個相結(jié)合
8、redis的同步刪除和異步刪除
同步刪除 : 刪除key時釋放value空間是在主線程中執(zhí)行飒硅。
異步刪除 : 刪除key時釋放value空間是在異步線程中執(zhí)行砂缩。
Redis服務(wù)自身對Key的刪除,可以分為「同步刪除」和「異步刪除」三娩。使用DEL命令會觸發(fā)「同步刪除」庵芭,如果Key是一個有很多元素的復(fù)雜類型,這個過程可能會堵塞一下Redis服務(wù)自身雀监,從而影響用戶的訪問双吆,所以對于bigkey不能直接del刪除。
同步和異步的區(qū)別就是釋放value空間是主線程去執(zhí)行還是異步線程去執(zhí)行会前,理解這句話很關(guān)鍵好乐。其他的操作都是由主線程執(zhí)行的。
4.0以上的版本瓦宜,默認是開啟異步刪除的蔚万,即lazyfree-lazy-expire=yes。
redis在進行key的過期刪除的時候临庇,如果開啟了異步刪除笛坦,則當被刪除的key所對應(yīng)的val占用空間大于64字節(jié)時区转,會將這個key標記為刪除后直接返回+OK,然后將val放到后臺的bio線程里面進行刪除版扩,防止阻塞主線程废离;
如果占用的空間小于64字節(jié),即使開啟了異步刪除礁芦,在最后運行的時候也會同步的進行刪除(redis優(yōu)秀的性能優(yōu)化在細節(jié)之末隨處可見蜻韭,針對很多場景都做了優(yōu)化,并抽象出參數(shù)給用戶動態(tài)配置柿扣,它的高性能是與redis作者精益求精的修改分不開的)肖方。
8、什么是緩存命中率未状?提高緩存命中率的方法有哪些俯画??d
- 命中:可以直接通過緩存獲取到需要的數(shù)據(jù)
- 不命中:無法直接通過緩存獲取到想要的數(shù)據(jù)司草,需要再次查詢數(shù)據(jù)庫或者執(zhí)行其他的操作艰垂,原因可能是由于緩存中根本不存在,或者緩存已經(jīng)過期
命中率越高表示使用緩存作用越好埋虹,性能越高(響應(yīng)時間越短猜憎,吞吐量越高),并發(fā)能力也越好搔课。
重點關(guān)注訪問評率高且時效性相對低一些的業(yè)務(wù)數(shù)據(jù)上胰柑,利用預(yù)加載(預(yù)熱)、擴容爬泥、優(yōu)化緩存麗都柬讨。更新緩存等手段來提高命中率
9、redis分布式鎖原理
9.1 單機模式
Redisson底層原理簡單描述:
先判斷一個key存在不存在袍啡,如果不存在踩官,則set key,同時設(shè)置過期時間和value(1)葬馋,
這個過程使用lua腳本來實現(xiàn)卖鲤,可以保證多個命令的原子性肾扰,當業(yè)務(wù)完成以后畴嘶,刪除key;
如果存在說明已經(jīng)有別的線程獲取鎖了集晚,那么就循環(huán)等待一段時間后再去獲取鎖
如果是可重入鎖呢:
先判斷一個key存在不存在窗悯,如果不存在,則set key偷拔,同時設(shè)置過期時間和value(線程id:1)蒋院,
如果存在亏钩,則判斷value中的線程id是否是當前線程的id,如果是欺旧,說明是可重入鎖姑丑,則value+1,變成(線程id:2)辞友,如果不是栅哀,說明是別的線程來獲取鎖,則獲取失敵屏留拾;這個過程同樣使用lua腳本一次性提交,保證原子性鲫尊。
如何防止業(yè)務(wù)還沒執(zhí)行完痴柔,但是鎖key過期呢,可以在線程加鎖成功后疫向,啟動一個后臺進程看門狗咳蔚,去定時檢查,如果線程還持有鎖鸿捧,就延長key的生存時間——Redisson就是這樣實現(xiàn)的屹篓。
其實Jedis也有現(xiàn)成的實現(xiàn)方式,單機匙奴、集群堆巧、分片都有實現(xiàn),底層原理是利用連用setnx泼菌、setex指令
(Redis從2.6之后支持setnx谍肤、setex連用)
jedis.set(key, value, "NX", "PX", expire)
注:setnx和setex都是原子性的
SETNX key value:
將 key 的值設(shè)為 value ,當且僅當 key 不存在哗伯;若給定的 key 已經(jīng)存在荒揣,則 SETNX 不做任何動作。
相當于是 EXISTS 焊刹、SET 兩個命令連用
SETEX key seconds value:
將value關(guān)聯(lián)到key, 并將key的生存時間設(shè)為seconds(以秒為單位)系任;如果key 已經(jīng)存在,SETEX將重寫舊值虐块;
相當于是SET俩滥、EXPIRE兩個命令連用
9.2 Cluster集群模式
很明顯,上面介紹的分布式鎖的實現(xiàn)只支持單機redis贺奠,工作中我們最常用的還是Cluster集群模式霜旧,上面的實現(xiàn)方式在集群模式下,是存在問題的儡率,Cluster集群模式介紹見Redis(四):集群模式
整個過程如下:
- 客戶端1在Redis的節(jié)點A上拿到了鎖挂据;
- 節(jié)點A宕機后以清,客戶端2發(fā)起獲取鎖key的請求,這時請求就會落在節(jié)點B上崎逃;
- 節(jié)點B由于之前并沒有存儲鎖key掷倔,所以客戶端2也可以成功獲取鎖,即客戶端1和客戶端2同時持有了同一個資源的鎖个绍。
針對這個問題今魔。Redis作者antirez提出了RedLock算法來解決這個問題
9.2.1 RedLock算法
RedLock算法思路如下:
獲取當前時間的毫秒數(shù)startTime;
按順序依次向N個Redis節(jié)點執(zhí)行獲取鎖的操作障贸,這個獲取鎖的操作和前面單Redis節(jié)點獲取鎖的過程相同错森,同時鎖超時時間應(yīng)該遠小于鎖的過期時間;
如果客戶端向某個Redis節(jié)點獲取鎖失敗/超時后篮洁,應(yīng)立即嘗試下一個Redis節(jié)點涩维;
失敗包括Redis節(jié)點不可用或者該Redis節(jié)點上的鎖已經(jīng)被其他客戶端持有如果客戶端成功獲取到超過半數(shù)的鎖時,記錄當前時間endTime袁波,同時計算整個獲取鎖過程的總耗時costTime = endTime - startTime瓦阐,如果獲取鎖總共消耗的時間遠小于鎖的過期時間(即costTime < expireTime),則認為客戶端獲取鎖成功篷牌,否則睡蟋,認為獲取鎖失敗
如果獲取鎖成功,需要重新計算鎖的過期時間枷颊。它等于最初鎖的有效時間減去第三步計算出來獲取鎖消耗的時間戳杀,即expireTime - costTime
如果最終獲取鎖失敗,那么客戶端立即向所有Redis發(fā)起釋放鎖的操作夭苗。(和單機釋放鎖的邏輯一樣)
9.2.2 缺陷
RedLock算法雖然可以解決單點Redis分布式鎖的安全性問題信卡,但如果集群中有節(jié)點發(fā)生崩潰重啟,還是會對鎖的安全性有影響的题造。
假設(shè)一共有5個Redis節(jié)點:A, B, C, D, E傍菇。設(shè)想發(fā)生了如下的事件序列:
- 客戶端1成功鎖住了A, B, C,獲取鎖成功(但D和E沒有鎖捉缗狻)丢习;
- 節(jié)點C崩潰重啟了,但客戶端1在C上加的鎖沒有持久化下來淮悼,丟失了咐低;
- 節(jié)點C重啟后,客戶端2鎖住了C, D, E敛惊,獲取鎖成功渊鞋;
這樣绰更,客戶端1和客戶端2同時獲得了鎖(針對同一資源)瞧挤。針對這樣場景锡宋,解決方式也很簡單,也就是讓Redis崩潰后延遲重啟特恬,并且這個延遲時間大于鎖的過期時間就好执俩。這樣等節(jié)點重啟后,所有節(jié)點上的鎖都已經(jīng)失效了癌刽。也不存在以上出現(xiàn)2個客戶端獲取同一個資源的情況了
還有一種情況役首,如果客戶端1獲取鎖后,訪問共享資源操作執(zhí)行任務(wù)時間過長(要么邏輯問題显拜,要么發(fā)生了GC)衡奥,導(dǎo)致鎖過期了,而后續(xù)客戶端2獲取鎖成功了远荠,這樣就會導(dǎo)致客戶端1和客戶端2同時操作共享資源矮固,相當于同一個時刻出現(xiàn)了2個客戶端獲得了鎖的情況。這也就是上面鎖過期時間要遠遠大于加鎖消耗的時間的原因譬淳。
服務(wù)器臺數(shù)越多档址,出現(xiàn)不可預(yù)期的情況也越多,所以針對分布式鎖的應(yīng)用的時候需要多測試邻梆。
如果系統(tǒng)對共享資源有非常嚴格要求得情況下守伸,還是建議需要做數(shù)據(jù)庫鎖的方案來補充,如飛機票或火車票座位得情況浦妄。
對于一些搶購獲取尼摹,針對偶爾出現(xiàn)超賣,后續(xù)可以通過人工介入來處理剂娄,畢竟redis節(jié)點不是天天奔潰窘问,同時數(shù)據(jù)庫鎖的方案
性能又低。
9.2.2 實現(xiàn)
redisson包已經(jīng)有對redlock算法封裝
public interface DistributedLock {
/**
* 獲取鎖
* @author zhi.li
* @return 鎖標識
*/
String acquire();
/**
* 釋放鎖
* @author zhi.li
* @param indentifier
* @return
*/
boolean release(String indentifier);
}
public class RedisDistributedRedLock implements DistributedLock {
/**
* redis 客戶端
*/
private RedissonClient redissonClient;
/**
* 分布式鎖的鍵值
*/
private String lockKey;
private RLock redLock;
/**
* 鎖的有效時間 10s
*/
int expireTime = 10 * 1000;
/**
* 獲取鎖的超時時間
*/
int acquireTimeout = 500;
public RedisDistributedRedLock(RedissonClient redissonClient, String lockKey) {
this.redissonClient = redissonClient;
this.lockKey = lockKey;
}
@Override
public String acquire() {
redLock = redissonClient.getLock(lockKey);
boolean isLock;
try{
isLock = redLock.tryLock(acquireTimeout, expireTime, TimeUnit.MILLISECONDS);
if(isLock){
System.out.println(Thread.currentThread().getName() + " " + lockKey + "獲得了鎖");
return null;
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
public boolean release(String indentifier) {
if(null != redLock){
redLock.unlock();
return true;
}
return false;
}
}
10宜咒、熱點緩存(熱key)
10.1 場景
假設(shè)你現(xiàn)在有 10 個緩存節(jié)點來抗?量的讀請求惠赫。正常情況下,讀請求應(yīng)該是均勻的落在 10 個緩存節(jié)點上的故黑,這 10 個緩存節(jié)點儿咱,每秒承載 1 萬請求是差不多的。
然后我們再做?個假設(shè)场晶,你?個節(jié)點承載 2 萬請求是極限混埠,所以?般你就限制?個節(jié)點正常承載 1 萬請求就 ok 了,稍微留?點 buffer 出來诗轻。
好钳宪,所謂的熱點緩存問題是什么意思呢?
很簡單,就是突然因為莫名的原因吏颖,出現(xiàn)?量的?戶訪問同?條緩存數(shù)據(jù)搔体。
舉個例?,某個明星突然宣布跟某某結(jié)婚半醉,這個時候是不是會引發(fā)可能短時間內(nèi)每秒都是數(shù)?
萬的?戶去查看這個明星跟某某結(jié)婚的那條新聞疚俱?
那么假設(shè)那條新聞就是?個緩存,然后對應(yīng)就是?個緩存 key缩多,就存在?臺緩存機器上呆奕,此時
瞬時假設(shè)有 20 萬請求奔向那?臺機器上的?個 key。
我們剛才假設(shè)的是?個緩存 Slave 節(jié)點最多每秒就是 2 萬的請求衬吆,當然實際緩存單機承載 5 萬~ 10 萬讀請求也是可能的梁钾,我們這?就是?個假設(shè)。結(jié)果此時逊抡,每秒突然奔過來 20 萬請求到這臺機器上陈轿,會怎么樣?
很簡單秦忿,那臺被 20 萬請求指向的緩存機器會過度操勞?宕機的麦射。
那么如果緩存集群開始出現(xiàn)機器的宕機,此時會如何灯谣?
接著潜秋,讀請求發(fā)現(xiàn)讀不到數(shù)據(jù),會從數(shù)據(jù)庫?提取原始數(shù)據(jù)胎许,然后放?剩余的其他緩存機器?
去峻呛。但是接踵?來的每秒 20 萬請求,會再次壓垮其他的緩存機器辜窑。
以此類推钩述,最終導(dǎo)致緩存集群全盤崩潰,引發(fā)系統(tǒng)整體宕機穆碎。
10.2 基于流式計算技術(shù)的緩存熱點?動發(fā)現(xiàn)
這?關(guān)鍵的?點牙勘,就是對于這種熱點緩存,你的系統(tǒng)需要能夠在熱點緩存突然發(fā)?的時
候所禀,直接發(fā)現(xiàn)他方面,然后瞬間??實現(xiàn)毫秒級的?動負載均衡。
那么我們就先來說說色徘,你如何?動發(fā)現(xiàn)熱點緩存問題恭金?
?先你要知道,?般出現(xiàn)緩存熱點的時候褂策,你的每秒并發(fā)肯定是很?的横腿,可能每秒都??萬甚
?上百萬的請求量過來颓屑,這都是有可能的。
所以耿焊,此時完全可以基于?數(shù)據(jù)領(lǐng)域的流式計算技術(shù)來進?實時數(shù)據(jù)訪問次數(shù)的統(tǒng)計揪惦,?如
storm、spark streaming搀别、flink,這些技術(shù)都是可以的尾抑。
然后?旦在實時數(shù)據(jù)訪問次數(shù)統(tǒng)計的過程中歇父,?如發(fā)現(xiàn)?秒之內(nèi),某條數(shù)據(jù)突然訪問次數(shù)超過
了 1000再愈,就直接??把這條數(shù)據(jù)判定為是熱點數(shù)據(jù)榜苫,可以將這個發(fā)現(xiàn)出來的熱點數(shù)據(jù)寫??如
zookeeper 中。
當然翎冲,你的系統(tǒng)如何判定熱點數(shù)據(jù)垂睬,可以根據(jù)??的業(yè)務(wù)還有經(jīng)驗值來就可以了。
當然肯定有?會問抗悍,那你的流式計算系統(tǒng)在進?數(shù)據(jù)訪問次數(shù)統(tǒng)計的時候驹饺,會不會也存在說單
臺機器被請求每秒??萬次的問題呢?
答案是否缴渊,因為流式計算技術(shù)赏壹,尤其是 storm 這種系統(tǒng),他可以做到同?條數(shù)據(jù)的請求過來衔沼,
先分散在很多機器?進?本地計算蝌借,最后再匯總局部計算結(jié)果到?臺機器進?全局匯總。
所以??萬請求可以先分散在?如 100 臺機器上指蚁,每臺機器統(tǒng)計了這條數(shù)據(jù)的?千次請求菩佑。
然后 100 條局部計算好的結(jié)果匯總到?臺機器做全局計算即可,所以基于流式計算技術(shù)來進?
統(tǒng)計是不會有熱點問題的
10.3 熱點緩存?動加載為 JVM 本地緩存
我們??的系統(tǒng)可以對 zookeeper 指定的熱點緩存對應(yīng)的 znode 進?監(jiān)聽凝化,如果有變化他??
就可以感知到了稍坯。
此時系統(tǒng)層就可以??把相關(guān)的緩存數(shù)據(jù)從數(shù)據(jù)庫加載出來,然后直接放在??系統(tǒng)內(nèi)部的本
地緩存?即可搓劫。
這個本地緩存劣光,你? ehcache、hashmap糟把,其實都可以绢涡,?切都看??的業(yè)務(wù)需求,主要說的
就是將緩存集群?的集中式緩存遣疯,直接變成每個系統(tǒng)??本地實現(xiàn)緩存即可雄可,每個系統(tǒng)??本
地是?法緩存過多數(shù)據(jù)的凿傅。
因為?般這種普通系統(tǒng)單實例部署機器可能就?個 4 核 8G 的機器,留給本地緩存的空間是很
少的数苫,所以?來放這種熱點數(shù)據(jù)的本地緩存是最合適的聪舒,剛剛好。
假設(shè)你的系統(tǒng)層集群部署了 100 臺機器虐急,那么好了箱残,此時你 100 臺機器瞬間在本地都會有?份
熱點緩存的副本。
然后接下來對熱點緩存的讀操作止吁,直接系統(tǒng)本地緩存讀出來就給返回了被辑,不?再?緩存集群
了。
這樣的話敬惦,也不可能允許每秒 20 萬的讀請求到達緩存機器的?臺機器上讀?個熱點緩存了盼理,?
是變成 100 臺機器每臺機器承載數(shù)千請求,那么那數(shù)千請求就直接從機器本地緩存返回數(shù)據(jù)
了俄删,這是沒有問題的宏怔。
10.4 限流熔斷保護
除此之外,在每個系統(tǒng)內(nèi)部畴椰,其實還應(yīng)該專?加?個對熱點數(shù)據(jù)訪問的限流熔斷保護措施臊诊。
每個系統(tǒng)實例內(nèi)部,都可以加?個熔斷保護機制斜脂,假設(shè)緩存集群最多每秒承載 4 萬讀請求妨猩,那
么你?共有 100 個系統(tǒng)實例。
你??就該限制好秽褒,每個系統(tǒng)實例每秒最多請求緩存集群讀操作不超過 400 次壶硅,?超過就可以
熔斷掉,不讓請求緩存集群销斟,直接返回?個空?信息庐椒,然后?戶稍后會??再次重新刷新??
之類的。
通過系統(tǒng)層??直接加限流熔斷保護措施蚂踊,可以很好的保護后?的緩存集群约谈、數(shù)據(jù)庫集群之類
的不要被打死,我們來看看下?的圖犁钟。