微服務分散式架構之redis篇

OneMoreWhy發表於2020-10-04
  1. 快速的redis有哪些慢操作?
  • 雜湊衝突導致時間複雜度向連結串列傾斜,拉低查詢效率。解決方案:雜湊桶rehash。
  • 一次性rehash會導致redis執行緒阻塞。解決方案:漸進式rehash,其實也是分步、分攤、分治思想。
  • 集合型別的範圍操作時間複雜度降為O(n)。解決方案:用SCAN命令代替,漸進式遍歷,每次只返回部分資料。
  • list型別由壓縮列表和雙向連結串列實現,時間複雜度都是O(n),但是這二者會記錄表頭和表尾的偏移量,適合用做先進先出的佇列使用。
  • 壓縮列表與陣列的區別是:壓縮列表會允許儲存資料的每一個元素的容量大小不同。
  1. 為什麼單執行緒的redis能那麼快?
  • 記憶體資料庫,記憶體操作
  • 單執行緒,無執行緒上下文切換開銷,無鎖資源效能開銷
  • 採用非阻塞IO多路複用技術
  • hash表的大量使用
  1. 當機了,redis如何避免資料丟失?
  • 資料持久化,兩種方式:RDB和AOF
  • RDB可以在指定的時間段內對資料進行快照儲存,方便傳輸,適用於災難備份和災難恢復。fork子程式,備份的工作由子程式來做。redis意外中斷時,會丟失資料。資料集大的時候,fork會比較耗時。
  • AOF將每次寫的操作追加到檔案末尾。AOF更加耐久,fsync策略可以自己設定。檔案體積大,速度可能會慢。
  • 在RDB和AOF之間如何抉擇?看業務需求,看中什麼優點,能接受什麼缺點,或者兩種方式結合起來用。
  • 可以呼叫save或者是bgsave命令進行手動備份。
  • 寫時複製機制。
  • AOF方式可以進行日誌重寫,優化,簡化。
  1. 當機後,redis如何實現快速恢復?
  • 有了RDB和AOF兩種持久化的方式,可以使用備份的檔案對redis資料進行恢復。
  1. 主從庫如何實現資料一致?
  • redis預設使用非同步複製
  • redis複製對於master而言,是非阻塞的。對於slave而言,載入新資料集是阻塞的。
  • 當master關閉了持久化和被允許自動重啟的時候,主從複製會面臨一個暗坑。master掛機,自動重啟,資料集為空,slave同步master的資料集後也變為空資料集。“天下武功,唯快不破”,master的重啟可以快到讓sentinel檢測不到。故即使配置了sentinel高可用,依然避免不了這個坑。所以在master關閉持久化的時候,需要禁止其自動重啟。
  • Replication ID, offset,主從之間同步靠的是這兩個引數。
  1. 主庫掛了,如何不間斷服務?
  • 哨兵模式,主庫掛了,能實現自動故障轉移,從從庫中選出一個庫升級為主庫。
  1. 哨兵掛了,主從庫還能切換嗎?
  • 如果哨兵只有一個節點,確實存在單點故障問題,一旦這個哨兵掛了,就不實現自動主從切換了
  • 但是哨兵一般也是需要配置多個節點,實現高可用的
  1. 資料增多了,是該加記憶體還是加例項?
  • 資料量增多了,可以考慮分片叢集,其實也是一種分而治之的思想。分而治之是處理大資料量一種很常見的思路。
  • 縱向擴充套件(scale up):升級單個 Redis 例項的資源配置,好理解,加記憶體,加磁碟,升CPU。縱向擴充套件的思路簡單粗暴,但是有明顯的缺點,面臨硬體成本,fork子程式會阻塞主執行緒,除非不要求持久化。
  • 橫向擴充套件(scale out):增加Redis例項的個數。橫向擴充套件沒有縱向擴充套件的問題,但是也會面臨其他的挑戰,多個例項如何管理?
  • 多個例項,如何決定某些資料入哪個例項?雜湊槽(Hash Slot)。在 Redis Cluster 方案中,一個切片叢集共有 16384 個雜湊槽,每個鍵值對都會根據它的 key,經過雜湊演算法(CRC16 演算法,然後對16384取模),被對映到一個雜湊槽中。
  • cluster create 命令建立叢集,redis自動把16384個槽平均分配在所有例項上。
  • cluster meet 命令手動建立例項間的連線,然後cluster addslots指定每個例項上的雜湊槽個數。
  • 這麼多例項,客戶端怎麼怎麼直到它某次需要操作哪個例項?redis多個例項相互連線之後,會同步各個例項上的雜湊槽資訊,使得每個redis例項都有叢集全量的雜湊槽資訊,然後,當客戶端和叢集建立連線後,叢集會把雜湊槽的分配資訊傳送給客戶端,客戶端通過同樣的演算法,最後定位到它要操作哪個例項。
  1. 快取滿了怎麼辦?
  • 通過記憶體快取淘汰演算法進行刪除一部分資料
  1. 如何解決快取擊穿、穿透和雪崩難題?
  • 快取擊穿:快取中沒有,但是資料庫中有。若併發使用者多,資料庫壓力瞬間增大。原因:熱點key過期。解決方案:設定熱點key永不過期;加互斥鎖,當執行緒A發現快取中資料沒有時會去資料庫中取,此時其他執行緒要取相同的key會等待,而不是重複取請求資料庫,再重複更新快取。
  • 快取穿透:快取和資料庫中都沒有資料,而使用者不斷髮起請求,此時可能是惡意攻擊。大量請求導致資料庫壓力增大。原因:極有可能是惡意攻擊。解決方案:介面層加校驗,儘量將惡意攻擊的請求資料儘早識別,儘早攔截,比較典型的方法是使用布隆過濾器對其進行過濾;將所請求的key的value值設定為null,可以將key的過期時間設定短一點,設定太長的話會導致該key在正常的情況下也無法使用,也就是誤殺,這樣可以防止惡意使用者反覆用同一條資料進行暴力攻擊。
  • 快取雪崩:快取中大量資料同時失效,導致大量請求直接請求資料庫,導致資料庫壓力增大。與擊穿區別:擊穿的場景是多個請求併發請求同一條資料,而雪崩指的是多個請求大量請求多條資料。原因:快取中大量資料同時過期。解決方案:各個key的過期時間隨機設定,避免多個key的過期時間設定為同一時間;設定熱點資料永不過期;分散式快取情況下,熱點資料均勻分佈。
  1. redis如何實現分散式鎖?
  • setnx命令加過期時間
  • 用redis做分散式鎖為什麼要用lua指令碼來釋放鎖?為了保證原子性。在釋放鎖的時候,需要考慮鎖釋放的鎖的value是不是當前執行緒設定的鎖的value,所有取value,然後比較,在然後刪除對應的key,這三步是需要作為一個原子操作的,而lua指令碼執行的時候就是單執行緒,就能保證這三個操作的原子性。
  1. 如何應對資料傾斜?
  • 針對hot key造成的資料傾斜,可以對hot key進行打散,可以對hot key加上字首或者是字尾,把一個key變成多個key,再通過分片演算法將資料儲存到不同的例項上,將訪問量分攤到各個不同的例項上。此時需要注意各個temp key的過期時間需要在原hot key的過期時間的基礎上,加上一個小的隨機正整數,這是為了防止多個key同時過期造成雪崩。
  • 針對big key造成的資料傾斜,可以進行拆分,如果big key對應的big value是個大json的形式,可以通過mset 的方式將這個 key 的內容打散分佈到各個例項中。
  1. redis支援哪些資料型別?
  • 字串、雜湊表、列表、集合、有序集合
  1. redis有哪些架構模式?各有什麼特點?
  • 單機,特點是容量受限、處理能力受限、單點故障、沒做到高可用
  • 主從複製,特點是可以在一定的程式上解決了容量問題和處理能力,讀壓力轉移給從庫了,但是還是存在單點故障問題,master寫的壓力問題還是沒有解決。
  • 哨兵模式,特點:sentinel監控各redis節點,當某個節點出問題,sentinel會通知其他節點,當主節點出問題時,sentinel會自動進行故障轉移操作,保障了高可用。缺點是切換時會需要時間並且丟資料,還是沒有解決master的寫壓力問題
  • 代理式叢集,對業務方而言,遮蔽後端多個redis複雜業務邏輯,但是針對代理層需要考慮高可用方案,擴縮容需要手動干預。
  • 3.0版本後,直連式叢集,Redis-Cluster採用無中心結構模式。優點:無中心節點模式,不會因為某個節點影響整體效能;節點間資料共享,可動態調整資料分佈;實現了可擴充套件性,節點可動態新增或刪除;實現了高可用;實現了故障自動轉移。缺點:各節點之間資源隔離性差,容易相互影響;資料之間非同步複製,不能保證強一致性。
  1. 使用redis做非同步佇列,有什麼缺點?
  • 使用List做非同步佇列,RPUSH生產訊息,LPOP消費訊息。生產者消費者模式,LPOP不等待佇列有值就直接消費,沒有消費到資料就認為是訊息被消費完畢。解決方案:應用層引入sleep機制,呼叫LPOP進行重試。
  • BLPOP會在沒有訊息的時候阻塞,直到訊息到來或者是超時。缺點:只能供一個消費者消費
  • pub/sub模式,釋出訂閱模式,缺點:訊息的釋出是無狀態的,無法保證訊息的可達。
  1. redis的記憶體淘汰策略有哪些?
  • noeviction:不刪除策略,達到最大記憶體限制時,再申請記憶體時,直接返回錯誤資訊
  • allkeys-lru:所有的key使用lru演算法,優先刪除最近最少使用的key
  • volatile-lru:對於設定了expire的key,使用lru演算法進行優先刪除
  • allkeys-random:對於所有的key,隨機刪除一部分key
  • volatile-random:對於設定了expire的key,隨機刪除一部分key
  • volatile-ttl:對於設定了expire的key, 優先刪除剩餘時間(time to live,TTL) 短的key
  • 如果沒有設定expire的key,volatile-lru、volatile-random、volatile-ttl和noeviction一樣
  • 具體選擇哪一種驅逐策略,需要根據業務場景來判斷。
  1. redis的併發競爭問題如何解決?
  • 解決併發競爭問題的總體思路就是加鎖,將併發執行變成順序執行,並行變成序列。
  • 業務客戶端加鎖,synchronized
  • 使用redis自帶的incr命令,保證原子性
  • 使用redis的watch命令,構建樂觀鎖
  • 使用redis的setnx實現內建的鎖
  • 分散式鎖,分散式鎖的實現方式有很多種
  1. 布隆過濾器是如何解決快取穿透的問題的?
  • 首先,布隆過濾器是什麼?布隆過濾器的作用是迅速地判斷一個key是否在某個集合中,類似於HashSet,但是與HashSet不同的是,使用布隆過濾器,不需要儲存key的值。
  • 布隆過濾器的原理是什麼?對一個key進行k個hash演算法,得到k個值。然後需要一個各位初始值為0的點陣圖,把點陣圖中這k個位置都置為1。在查詢的時候,用同樣的演算法,校驗這k個位置是否都為1,如果是,就說明該key存在,否則則認為該key不存在。
  • 布隆過濾器的優缺點是什麼?優點:不需要儲存key,節省空間,運算都是用hash函式,運算速度快,避免了無謂地查詢redis和資料庫的開銷;缺點:可能誤判,可能把不存在的key判定為存在,但是概率比較小。redis的點陣圖只支援2的32方大小,對應於記憶體中也就是512MB,誤判率為萬分之一。另外,布隆過濾器不支援刪除操作。
  • 布隆過濾器是如何解決快取穿透的問題的?流程:查詢key請求過來,先去redis裡面查,如果查不到,用布隆過濾器判定該key是否存在,不存在直接返回null,如果存在就去資料庫取,取到後寫回redis,然後返回給客戶端。
  • 布隆過濾器還有其他的應用場景嗎?有的:網頁爬蟲對URL去重;反垃圾郵件;反垃圾簡訊;訊息推送去重;推薦系統去重。
  • 布隆過濾器在程式碼層面如何用?google的guava包已經有相關的API。
  1. redis的紅鎖是有什麼作用?
  • 用來實現分散式鎖。
  • 有什麼優點?高可用
  • 紅鎖的加鎖流程是怎麼樣的?有2n + 1個節點,這些節點之間是沒有主從也沒有叢集關係,客戶端使用相同的key和隨機值在這2n + 1個節點中請求鎖,注意請求鎖的超時時間應該小於鎖自動釋放的時間,當超過半數請求到鎖的時候,才算是真正加鎖成功,如果沒有加鎖成功,則回滾釋放已經鎖定的節點。
  1. redisson是幹什麼用的?有什麼優點?
  • redisson是一個在Redis的基礎上實現的Java駐記憶體資料網格,功能非常強大。
  • 可以用redisson實現分散式鎖,可以用watch dog機制來避免業務執行時間超過鎖過期時間而產生的鎖釋放的問題。

相關文章