1. 程式人生 > >java架構之路-(Redis專題)聊聊大廠那些redis

java架構之路-(Redis專題)聊聊大廠那些redis

  上幾次說了redis的主從,哨兵,叢集配置,但是內部的選舉一直沒說,先來簡單說一下選舉吧。

叢集選舉

  redis cluster節點間採取gossip協議進行通訊,也就是說,在每一個節點間,無論主節點還是從節點,他們之間都是存在相互通訊的。例如你的redis埠號是6379,那麼你的gossip協議埠號就是16379。

  gossip協議包含多種訊息,包括ping,pong,meet,fail等等。

  ping:每個節點都會頻繁給其他節點發送ping,其中包含自己的狀態還有自己維護的叢集元資料,互相通過ping交換元 資料;

  pong: 返回ping和meet,包含自己的狀態和其他資訊,也可以用於資訊廣播和更新;

  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的優化與實際場景

快取穿透

  我們在一般大型的網際網路專案查詢到的資料,都是查詢的快取內的資料,也就是我們的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);
    }
    return goods;
}

  如果是黑客來了,一直拿不同的快取來請求我們的專案,這樣的思路是不可取的,我們可以使用布隆過濾器來實現阻止快取擊穿問題。

快取預熱

  雙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差不多就說這麼多了,再深入的c語言程式碼,我也不懂了,我們大概簡單使用,基礎的搭建主從,哨兵,叢集,java連結redis,redis的優化這幾個角度來講解我們的redis,後面我會弄一篇redis的面試題,也是圍繞這些來講解的,還是那句話,真正懂得了內部的原理,什麼樣的面試題都不在話下了...

 

最進弄了一個公眾號,小菜技術,歡迎大家的加入