上幾次說了redis的主從,哨兵,集群配置,但是內(nèi)部的選舉一直沒說,先來簡單說一下選舉吧。
集群選舉
redis
cluster節(jié)點間采取gossip協(xié)議進行通信,也就是說,在每一個節(jié)點間,無論主節(jié)點還是從節(jié)點,他們之間都是存在相互通信的。例如你的redis端口號是6379,那么你的gossip協(xié)議端口號就是16379。
gossip協(xié)議包含多種消息,包括ping,pong,meet,fail等等。
ping:每個節(jié)點都會頻繁給其他節(jié)點發(fā)送ping,其中包含自己的狀態(tài)還有自己維護的集群元數(shù)據(jù),互相通過ping交換元 數(shù)據(jù);
pong: 返回ping和meet,包含自己的狀態(tài)和其他信息,也可以用于信息廣播和更新;
fail: 某個節(jié)點判斷另一個節(jié)點fail之后,就發(fā)送fail給其他節(jié)點,通知其他節(jié)點,指定的節(jié)點宕機了。
meet:某個節(jié)點發(fā)送meet給新加入的節(jié)點,讓新節(jié)點加入集群中,然后新節(jié)點就會開始與其他節(jié)點進行通信,不需要 發(fā)送形成網(wǎng)絡的所需的所有CLUSTER
MEET命令。發(fā)送CLUSTER MEET消息以便每個節(jié)點能夠達到其他每個節(jié)點只需通
過一條已知的節(jié)點鏈就夠了。由于在心跳包中會交換gossip信息,將會創(chuàng)建節(jié)點間缺失的鏈接。
當我們的master節(jié)點和其slave節(jié)點中斷,或和其它節(jié)點中斷時,也就是連接超過了我們設置的cluster‐node‐timeout的值,這時就會認為我們的當前的master是不可用的,需要選舉了,這時將自己記錄的集群currentEpoch加1,并廣播FAILOVER_AUTH_REQUEST信息到所有節(jié)點上(包括其他主從的從節(jié)點),其它主節(jié)點收到FAILOVER_AUTH_REQUEST信息會給與一個FAILOVER_AUTH_ACK反饋,其它從節(jié)點不會有任何反應,當我們的slave收到ACK反饋達到半數(shù)以上時,會當選當前選舉內(nèi)的master節(jié)點,其它slave節(jié)點不在進行選舉,作為該新master的slave節(jié)點。廣播Pong消息通知其他集群節(jié)點。
流程大概就是這樣的,還有可能每個正在選舉的slave節(jié)點收到的ACK反饋是一樣的,這時再次觸發(fā)一次選舉,currentEpoch再加1,流程和上面一樣。這里要注意的是,并不是每個slave都在同一時刻向外發(fā)送FAILOVER_AUTH_REQUEST信息的,一般數(shù)據(jù)較新的節(jié)點會先發(fā),數(shù)據(jù)的新舊由SLAVE_RANK來判斷,SLAVE_RANK越小,代表數(shù)據(jù)越新。
?Redis的優(yōu)化與實際場景
緩存穿透
我們在一般大型的互聯(lián)網(wǎng)項目查詢到的數(shù)據(jù),都是查詢的緩存內(nèi)的數(shù)據(jù),也就是我們的redis內(nèi)的數(shù)據(jù),但是第一次查詢或者說根本不存在的數(shù)據(jù),會穿過緩存到達我們的數(shù)據(jù)庫去查詢,如果大量這樣的請求過來,我們的數(shù)據(jù)庫是扛不住的。這就是我們常說的緩存穿透。處理思路很簡單,只要是請求過來的,沒有結果。存入緩存設置超時時間,再返回。設置時間是為了保證現(xiàn)在沒用到,現(xiàn)在沒緩存結果,不代表永遠沒有緩存結果。
@GetMapping(value = "/") public String getIndex(String goods_id){ //優(yōu)先從緩存去拿
String goods = stringRedisTemplate.opsForValue().get(goods_id); if (goods ==
null){ //如果拿不到去數(shù)據(jù)庫拿 goods = goodsService.getGoodsById(goods_id); //存入緩存,設置超時時間
stringRedisTemplate.opsForValue().set(goods_id,goods,300); } return goods; }
如果是黑客來了,一直拿不同的緩存來請求我們的項目,這樣的思路是不可取的,我們可以使用布隆過濾器來實現(xiàn)阻止緩存擊穿問題。
緩存預熱
雙11要來了,每次雙11的0點,會有大批的商品進行交易,如果這些商品不是存在緩存內(nèi)的,超高的并發(fā)(都不用雙11,平時的秒殺就夠受的),大量的線程會涌入數(shù)據(jù)庫,給數(shù)據(jù)庫造成超大的壓力,我們這時應該提前將這些要秒殺的商品,提前存入到redis當中去,防止大批量的請求直接沖進數(shù)據(jù)庫。這就是我們提到的緩存預熱。
緩存失效
剛才我們的說了預熱,但是我還是需要設置超時時間的時間的,不設置超時時間的話,你的數(shù)據(jù)庫更新了,而我們的緩存還是我們的最開始的數(shù)據(jù),造成數(shù)據(jù)的不一致。假設我們在預熱的時候?qū)⒋罅康纳唐吩O置為300秒超時的時間,開始秒殺....過了300秒了。還是有一定的并發(fā)量,這時所有的緩存都失效了,還是會有大量的請求進入到我們的數(shù)據(jù)庫的,所以說我在設置緩存預熱時,不要設置同一個時間結束。會造成大量的緩存在同一時間失效,給我們的后臺服務造成巨大壓力。
緩存雪崩
有很多項目還是在停留在使用redis單機的狀態(tài),如果說redis不在對我們的項目服務了,大量的請求會涌入我們的數(shù)據(jù)訪問層,造成我們的數(shù)據(jù)庫壓力超大,甚至數(shù)據(jù)庫宕機,從導致整個服務的不可用狀態(tài)?;蛘哒f我們的并發(fā)量遠遠超過我們的redis吞吐量。也會早成redis的擁塞,其它線程請求redis超時,早成redis假死現(xiàn)象,造成我們的redis雪崩。這時我們應該盡力采用高可用的緩存層架構,比如哨兵,比如集群架構,對于并發(fā)量超大的情況我們可以使用限流的方式來控制。
熱點緩存重建
如果說,我們的設置了一個緩存,失效時間為300毫秒,但在失效那一刻,還是高并發(fā)的狀態(tài),我們的服務器壓力還是巨大的,這些高并發(fā)的請求進入我們的數(shù)據(jù)庫,后果可想而知,所以我們要在這個熱點key的重建過程中,避免大量的請求進入我們的數(shù)據(jù)庫。我們可以這樣來做,嘗試加一把簡單的鎖。
@GetMapping(value = "/") public String getIndex(String goods_id) throws
InterruptedException {//優(yōu)先從緩存去拿 String goods =
stringRedisTemplate.opsForValue().get(goods_id);if (goods == null){ //如果拿不到去數(shù)據(jù)庫拿
//設置只有一個請求可以進入數(shù)據(jù)庫,其余的線程自旋等待 Boolean aBoolean =
stringRedisTemplate.opsForValue().setIfAbsent("lock" + goods_id, goods_id,
Duration.ofMinutes(3)); if(aBoolean){ goods =
goodsService.getGoodsById(goods_id);//存入緩存,設置超時時間
stringRedisTemplate.opsForValue().set(goods_id,goods,300); }else{ //自旋等待50毫秒
Thread.sleep(50); //再次調(diào)用該方法,嘗試獲取數(shù)據(jù) getIndex(goods_id); } } return goods; }
一些Redis的使用建議
1.建議key設置為服務名:表名或者模塊名:表名作為key,便于后期的查找和使用。
2.保證能識別語義的前提下,盡力設置key要簡潔,不要過長。
3.不要在key中設置特殊字符,比如空格、換行等字符。
4.redis中不要設置過大的值,一個字符串最大限制512M,但建議一般是要超過10kb大小,list,set,hash,zset不建議超過5000個元素,視情況而定。
5.不要使用keys命令,建議使用scan命令進行替換。
6.建議多使用原生命令,管道等操作盡力減少使用,推薦使用mget,mset這樣的命令。
這些優(yōu)化其實都是圍繞著我們Redis的特性,單線程來說的,如果說我們存了一個bigKey或者是一次性塞入了超多的命令,很可能阻塞后面的命令,造成我們的redis假死現(xiàn)象,也會造成我們的網(wǎng)絡擁塞,占有了更多的帶寬。
Redis的清除策略
1.被動刪除:當讀/寫一個已經(jīng)過期的key時,會觸發(fā)惰性刪除策略,直接刪除掉這個過期key
2.主動刪除:由于惰性刪除策略無法保證冷數(shù)據(jù)被及時刪掉,所以Redis會定期主動淘汰一批已過期的key
3.當前已用內(nèi)存超過maxmemory限定時,觸發(fā)主動刪除策略。
在redis啟動前,我們就配置了,最大的內(nèi)存使用maxmemory,當前已用內(nèi)存超過maxmemory限定時,會觸發(fā)主動清理策略。
默認策略是volatile-lru,即超過最大內(nèi)存后,在過期鍵中使用lru算法進行key的剔除,保證不過 期數(shù)據(jù)不被刪除,但是可能會出現(xiàn)OOM問題。
其他策略如下:
allkeys-lru:根據(jù)LRU算法刪除鍵,不管數(shù)據(jù)有沒有設置超時屬性,直到騰出足夠空間 為止。
allkeys-random:隨機刪除所有鍵,直到騰出足夠空間為止。
volatile-random: 隨機刪除過期鍵,直到騰出足夠空間為止。
volatile-ttl:根據(jù)鍵值對象的ttl屬性,刪除最近將要過期數(shù)據(jù)。如果沒有,回退到
noeviction策略。 noeviction:不會剔除任何數(shù)據(jù),拒絕所有寫入操作并返回客戶端錯誤信息"(error)OOM command not
allowed when used memory",此時Redis只響應讀操作。
注意:如果沒有配置我們的maxmemory屬性,當我們的內(nèi)存寫滿以后,不會觸發(fā)任何清除策略,會直接將我們的數(shù)據(jù)存放在磁盤上,極具降低我們的redis性能。
總結:
redis差不多就說這么多了,再深入的c語言代碼,我也不懂了,我們大概簡單使用,基礎的搭建主從,哨兵,集群,java鏈接redis,redis的優(yōu)化這幾個角度來講解我們的redis,后面我會弄一篇redis的面試題,也是圍繞這些來講解的,還是那句話,真正懂得了內(nèi)部的原理,什么樣的面試題都不在話下了...
?
最進弄了一個公眾號,小菜技術,歡迎大家的加入
熱門工具 換一換