上幾次說了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的面試題,也是圍繞這些來講解的,還是那句話,真正懂得了內部的原理,什么樣的面試題都不在話下了...
最進弄了一個公眾號,小菜技術,歡迎大家的加入