Redis續一

1、Redis 在內存使用上是如何開源節流算法

Redis 跟其餘傳統數據庫不一樣,Redis 是一個純內存的數據庫,而且存儲了都是一些數據結構的數據,若是不對內存加以控制的話,Redis 極可能會由於數據量過大致使系統的奔潰。數據庫

一、Ziplist安全

當最開始嘗試開啓一個小數據量的 Hash 結構和一個 Zset 結構時,發現他們在 Redis 裏面的真正結構類型是一個 Ziplist。服務器

Ziplist 是一個緊湊的數據結構,每個元素之間都是連續的內存,若是在 Redis 中,Redis 啓用的數據結構數據量很小時,Redis 就會切換到使用緊湊存儲的形式來進行壓縮存儲。網絡

例如,上面的例子,咱們採用了 Hash 結構進行存儲,Hash 結構是一個二維的結構,是一個典型的用空間換取時間的結構。數據結構

可是若是使用的數據量很小,使用二維結構反而浪費了空間,在時間的性能上也並無獲得太大的提高,還不如直接使用一維結構進行存儲。併發

在查找的時候,雖然複雜度是 O(n),可是由於數據量少遍歷也很是快,增至比 Hash 結構自己的查詢更快。函數

若是當集合對象的元素不斷的增長,或者某個 Value 的值過大,這種小對象存儲也會升級生成標準的結構。性能

Redis 也能夠在配置中進行定義緊湊結構和標準結構的轉換參數:ui

二、Quicklist

Quicklist 數據結構是 Redis 在 3.2 才引入的一個雙向鏈表的數據結構,確實來講是一個 Ziplist 的雙向鏈表。

Quicklist 的每個數據節點是一個 Ziplist,Ziplist 自己就是一個緊湊列表。

假使,Quicklist 包含了 5 個 Ziplist 的節點,每一個 Ziplist 列表又包含了 5 個數據,那麼在外部看來,這個 Quicklist 就包含了 25 個數據項。

Quicklist 的結構設計簡單總結起來,是一個空間和時間的折中方案:

  • 雙向鏈表能夠在兩端進行 Push 和 Pop 操做,可是它在每個節點除了保存自身的數據外,還要保存兩個指針,增長額外的內存開銷。

其次是因爲每一個節點都是獨立的,在內存地址上並不連續,節點多了容易產生內存碎片。

  • Ziplist 自己是一塊連續的內存,存儲和查詢效率很高,可是,它不利於修改操做,每次數據變更時都會引起內存 Realloc,若是 Ziplist 長度很長時,一次 Realloc 會致使大批量數據拷貝。

因此,結合 Ziplist 和雙向鏈表的優勢,Quciklist 就孕育而生。

三、對象共享

Redis 在本身的對象系統中構建了一個引用計數方法,經過這個方法程序能夠跟蹤對象的引用計數信息,除了能夠在適當的時候進行對象釋放,還能夠用來做爲對象共享。

舉個例子,假使鍵 A 建立了一個整數值 100 的字符串做爲值對象,這個時候鍵 B 也建立保存一樣整數值 100 的字符串對象做爲值對象。

那麼在 Redis 的操做時:

  • 將數據庫鍵的指針指向一個現有的值對象。
  • 將被共享的值對象引用計數加一。

假使,咱們的數據庫中指向整數值 100 的鍵不止鍵 A 和鍵 B,而是有幾百個,那麼 Redis 服務器中只須要一個字符串對象的內存就能夠保存本來須要幾百個字符串對象的內存才能保存的數據。

2、Redis 是如何實現主從複製

幾個定義:

  • runID:服務器運行的 ID。
  • Offset:主服務器的複製偏移量和從服務器複製的偏移量。
  • Replication backlog:主服務器的複製積壓緩衝區。

在 Redis 2.8 以後,使用 Psync 命令代替 Sync 命令來執行復制的同步操做。

Psync 命令具備完整重同步和部分重同步兩種模式:

  • 完整同步用於處理初次複製狀況:完整重同步的執行步驟和 Sync 命令執行步驟一致,都是經過讓主服務器建立併發送 RDB 文件,以及向從服務器發送保存在緩衝區的寫命令來進行同步。
  • 部分重同步是用於處理斷線後重複製狀況:當從服務器在斷線後從新鏈接主服務器時,主服務能夠將主從服務器鏈接斷開期間執行的寫命令發送給從服務器,從服務器只要接收並執行這些寫命令,就能夠將數據庫更新至主服務器當前所處的狀態。

完整重同步:

  • Slave 發送 Psync 給 Master,因爲是第一次發送,不帶上 runID 和 Offset。
  • Master 接收到請求,發送 Master 的 runID 和 Offset 給從節點。
  • Master 生成保存 RDB 文件。
  • Master 發送 RDB 文件給 Slave。
  • 在發送 RDB 這個操做的同時,寫操做會複製到緩衝區 Replication Backlog Buffer 中,並從 Buffer 區發送到 Slave。
  • Slave 將 RDB 文件的數據裝載,並更新自身數據。

若是網絡的抖動或者是短期的斷鏈也須要進行完整同步就會致使大量的開銷,這些開銷包括了,Bgsave 的時間,RDB 文件傳輸的時間,Slave 從新加載 RDB 時間,若是 Slave 有 AOF,還會致使 AOF 重寫。

這些都是大量的開銷,因此在 Redis 2.8 以後也實現了部分重同步的機制。

部分重同步:

  • 網絡發生錯誤,Master 和 Slave 失去鏈接。
  • Master 依然向 Buffer 緩衝區寫入數據。
  • Slave 從新鏈接上 Master。
  • Slave 向 Master 發送本身目前的 runID 和 Offset。
  • Master 會判斷 Slave 發送給本身的 Offset 是否存在 Buffer 隊列中。
  • 若是存在,則發送 Continue 給 Slave;若是不存在,意味着可能錯誤了太多的數據,緩衝區已經被清空,這個時候就須要從新進行全量的複製。
  • Master 發送從 Offset 偏移後的緩衝區數據給 Slave。
  • Slave 獲取數據更新自身數據。

3、Redis 是怎麼制定過時刪除策略的

當一個鍵處於過時的狀態,其實在 Redis 中這個內存並非實時就被從內存中進行摘除,而是 Redis 經過必定的機制去把一些處於過時鍵進行移除,進而達到內存的釋放,那麼當一個鍵處於過時,Redis 會在何時去刪除?

幾時被刪除存在三種可能性,這三種可能性也表明了 Redis 的三種不一樣的刪除策略。

  • 定時刪除:在設置鍵過去的時間同時,建立一個定時器,讓定時器在鍵過時時間來臨,當即執行對鍵的刪除操做。
  • 惰性刪除:聽任鍵過時無論,可是每次從鍵空間獲取鍵時,都會檢查該鍵是否過時,若是過時的話,就刪除該鍵。
  • 按期刪除:每隔一段時間,程序都要對數據庫進行一次檢查,刪除裏面的過時鍵,至於要刪除多少過時鍵,由算法而定。

一、定時刪除

設置鍵的過時時間,建立定時器,一旦過時時間來臨,就當即對鍵進行操做。

這種對內存是友好的,可是對 CPU 的時間是最不友好的,特別是在業務繁忙,過時鍵不少的時候,刪除過時鍵這個操做就會佔據很大一部分 CPU 的時間。

要知道 Redis 是單線程操做,在內存不緊張而 CPU 緊張的時候,將 CPU 的時間浪費在與業務無關的刪除過時鍵上面,會對 Redis 的服務器的響應時間和吞吐量形成影響。

另外,建立一個定時器須要用到 Redis 服務器中的時間事件,而當前時間事件的實現方式是無序鏈表,時間複雜度爲 O(n),讓服務器大量建立定時器去實現定時刪除策略,會產生較大的性能影響,因此,定時刪除並非一種好的刪除策略。

二、惰性刪除

與定時刪除相反,惰性刪除策略對 CPU 來講是最友好的,程序只有在取出鍵的時候纔會進行檢查,是一種被動的過程。

與此同時,惰性刪除對內存來講又是最不友好的,一個鍵過時,只要再也不被取出,這個過時鍵就不會被刪除,它佔用的內存也不會被釋放。

很明顯,惰性刪除也不是一個很好的策略,Redis 是很是依賴內存和較好內存的,若是一些長期鍵長期沒有被訪問,就會形成大量的內存垃圾,甚至會操成內存的泄漏。

在對執行數據寫入時,經過 expireIfNeeded 函數對寫入的 Key 進行過時判斷。

其中 expireIfNeeded 在內部作了三件事情,分別是:

  • 查看 Key 是否過時。
  • 向 Slave 節點傳播執行過去 Key 的動做。
  • 刪除過時 Key。

三、按期刪除

上面兩種刪除策略,不管是定時刪除和惰性刪除,這兩種刪除方式在單一的使用上都存在明顯的缺陷,要麼佔用太多 CPU 時間,要麼浪費太多內存。

按期刪除策略是前兩種策略的一個整合和折中:

  • 按期刪除策略每隔一段時間執行一次刪除過時鍵操做,並經過限制刪除操做執行的時間和頻率來減小刪除操做對 CPU 時間的影響。
  • 經過合理的刪除執行的時長和頻率,來達到合理的刪除過時鍵。

note: 一我的真正的安全感,來自於心裏的自信。

相關文章
相關標籤/搜索