redis cluster節點間採起gossip協議進行通訊,也就是說,在每個節點間,不管主節點仍是從節點,他們之間都是存在相互通訊的。例如你的redis端口號是6379,那麼你的gossip協議端口號就是16379。java
gossip協議包含多種消息,包括ping,pong,meet,fail等等。node
ping:每一個節點都會頻繁給其餘節點發送ping,其中包含本身的狀態還有本身維護的集羣元數據,互相經過ping交換元 數據;面試
pong: 返回ping和meet,包含本身的狀態和其餘信息,也能夠用於信息廣播和更新;redis
fail: 某個節點判斷另外一個節點fail以後,就發送fail給其餘節點,通知其餘節點,指定的節點宕機了。算法
meet:某個節點發送meet給新加入的節點,讓新節點加入集羣中,而後新節點就會開始與其餘節點進行通訊,不須要 發送造成網絡的所需的全部CLUSTER MEET命令。發送CLUSTER MEET消息以便每一個節點可以達到其餘每一個節點只需通 過一條已知的節點鏈就夠了。因爲在心跳包中會交換gossip信息,將會建立節點間缺失的連接。數據庫
當咱們的master節點和其slave節點中斷,或和其它節點中斷時,也就是鏈接超過了咱們設置的cluster‐node‐timeout的值,這時就會認爲咱們的當前的master是不可用的,須要選舉了,這時將本身記錄的集羣currentEpoch加1,並廣播FAILOVER_AUTH_REQUEST信息到全部節點上(包括其餘主從的從節點),其它主節點收到FAILOVER_AUTH_REQUEST信息會給與一個FAILOVER_AUTH_ACK反饋,其它從節點不會有任何反應,當咱們的slave收到ACK反饋達到半數以上時,會當選當前選舉內的master節點,其它slave節點不在進行選舉,做爲該新master的slave節點。廣播Pong消息通知其餘集羣節點。緩存
流程大概就是這樣的,還有可能每一個正在選舉的slave節點收到的ACK反饋是同樣的,這時再次觸發一次選舉,currentEpoch再加1,流程和上面同樣。這裏要注意的是,並非每一個slave都在同一時刻向外發送FAILOVER_AUTH_REQUEST信息的,通常數據較新的節點會先發,數據的新舊由SLAVE_RANK來判斷,SLAVE_RANK越小,表明數據越新。服務器
緩存穿透網絡
咱們在通常大型的互聯網項目查詢到的數據,都是查詢的緩存內的數據,也就是咱們的redis內的數據,可是第一次查詢或者說根本不存在的數據,會穿過緩存到達咱們的數據庫去查詢,若是大量這樣的請求過來,咱們的數據庫是扛不住的。這就是咱們常說的緩存穿透。處理思路很簡單,只要是請求過來的,沒有結果。存入緩存設置超時時間,再返回。設置時間是爲了保證如今沒用到,如今沒緩存結果,不表明永遠沒有緩存結果。架構
@GetMapping(value = "/")public String getIndex(String goods_id){ //優先從緩存去拿 String goods =stringRedisTemplate.opsForValue().get(goods_id); if (goods == null){ //若是拿不到去數據庫拿 goods = goodsService.getGoodsById(goods_id);//存入緩存,設置超時時間stringRedisTemplate.opsForValue().set(goods_id,goods,300); }returngoods;}
若是是***來了,一直拿不一樣的緩存來請求咱們的項目,這樣的思路是不可取的,咱們可使用布隆過濾器來實現阻止緩存擊穿問題。
緩存預熱
雙11要來了,每次雙11的0點,會有大批的商品進行交易,若是這些商品不是存在緩存內的,超高的併發(都不用雙11,平時的秒殺就夠受的),大量的線程會涌入數據庫,給數據庫形成超大的壓力,咱們這時應該提早將這些要秒殺的商品,提早存入到redis當中去,防止大批量的請求直接衝進數據庫。這就是咱們提到的緩存預熱。
緩存失效
剛纔咱們的說了預熱,可是我仍是須要設置超時時間的時間的,不設置超時時間的話,你的數據庫更新了,而咱們的緩存仍是咱們的最開始的數據,形成數據的不一致。假設咱們在預熱的時候將大量的商品設置爲300秒超時的時間,開始秒殺....過了300秒了。仍是有必定的併發量,這時全部的緩存都失效了,仍是會有大量的請求進入到咱們的數據庫的,因此說我在設置緩存預熱時,不要設置同一個時間結束。會形成大量的緩存在同一時間失效,給咱們的後臺服務形成巨大壓力。
緩存雪崩
有不少項目仍是在停留在使用redis單機的狀態,若是說redis不在對咱們的項目服務了,大量的請求會涌入咱們的數據訪問層,形成咱們的數據庫壓力超大,甚至數據庫宕機,從致使整個服務的不可用狀態。或者說咱們的併發量遠遠超過咱們的redis吞吐量。也會早成redis的擁塞,其它線程請求redis超時,早成redis假死現象,形成咱們的redis雪崩。這時咱們應該盡力採用高可用的緩存層架構,好比哨兵,好比集羣架構,對於併發量超大的狀況咱們可使用限流的方式來控制。
若是說,咱們的設置了一個緩存,失效時間爲300毫秒,但在失效那一刻,仍是高併發的狀態,咱們的服務器壓力仍是巨大的,這些高併發的請求進入咱們的數據庫,後果可想而知,因此咱們要在這個熱點key的重建過程當中,避免大量的請求進入咱們的數據庫。咱們能夠這樣來作,嘗試加一把簡單的鎖。
@GetMapping(value = "/")public String getIndex(String goods_id) throws InterruptedException { //優先從緩存去拿 String goods = stringRedisTemplate.opsForValue().get(goods_id);if (goods == null){//若是拿不到去數據庫拿//設置只有一個請求能夠進入數據庫,其他的線程自旋等待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);//再次調用該方法,嘗試獲取數據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這樣的命令。
這些優化其實都是圍繞着咱們Redis的特性,單線程來講的,若是說咱們存了一個bigKey或者是一次性塞入了超多的命令,極可能阻塞後面的命令,形成咱們的redis假死現象,也會形成咱們的網絡擁塞,佔有了更多的帶寬。
Redis的清除策略
1.被動刪除:當讀/寫一個已通過期的key時,會觸發惰性刪除策略,直接刪除掉這個過時key
2.主動刪除:因爲惰性刪除策略沒法保證冷數據被及時刪掉,因此Redis會按期主動淘汰一批已過時的key
3.當前已用內存超過maxmemory限定時,觸發主動刪除策略。
在redis啓動前,咱們就配置了,最大的內存使用maxmemory,當前已用內存超過maxmemory限定時,會觸發主動清理策略。
默認策略是volatile-lru,即超過最大內存後,在過時鍵中使用lru算法進行key的剔除,保證不過 期數據不被刪除,可是可能會出現OOM問題。
其餘策略以下:
allkeys-lru:根據LRU算法刪除鍵,無論數據有沒有設置超時屬性,直到騰出足夠空間 爲止。
allkeys-random:隨機刪除全部鍵,直到騰出足夠空間爲止。
volatile-random: 隨機刪除過時鍵,直到騰出足夠空間爲止。
volatile-ttl:根據鍵值對象的ttl屬性,刪除最近將要過時數據。若是沒有,回退到
noeviction策略。 noeviction:不會剔除任何數據,拒絕全部寫入操做並返回客戶端錯誤信息"(error)OOM command not allowed when used memory",此時Redis只響應讀操做。
注意:若是沒有配置咱們的maxmemory屬性,當咱們的內存寫滿之後,不會觸發任何清除策略,會直接將咱們的數據存放在磁盤上,極具下降咱們的redis性能。
redis差很少就說這麼多了,咱們大概簡單使用,基礎的搭建主從,哨兵,集羣,java連接redis,redis的優化這幾個角度來說解咱們的redis,後面我會弄一篇redis的面試題,也是圍繞這些來說解的,仍是那句話,真正懂得了內部的原理,什麼樣的面試題都不在話下了...