Redis幾個問題總結

redis持久化策略

redis是一個內存數據庫,但是它提供了持久化機制。即把數據永久的存儲在磁盤上。我們來看看這個redis保存數據的流程

(1)客戶端向服務端發送寫操作(數據在客戶端的內存中)。

(2)數據庫服務端接收到寫請求的數據(數據在服務端的內存中)。

(3)服務端調用write這個系統調用,將數據往磁盤上寫(數據在系統內存的緩衝區中)。

(4)操作系統將緩衝區中的數據轉移到磁盤控制器上(數據在磁盤緩存中)。

(5)磁盤控制器將數據寫到磁盤的物理介質中(數據真正落到磁盤上)。

這裏有五步,正常情況下是能夠把數據持久化到磁盤的,但是在大多數情況下,我們的機器等等都會有各種各樣的故障,那麼數據就可能損壞,數據損壞之後,redis是怎麼回覆的呢,這就涉及到redis的持久化策略

RDB機制

RDB其實就是把數據以快照的形式保存在磁盤上,既然需要存數據,那麼RDB機制是怎麼處罰的呢?即redis何時纔會把數據以快照的形象保存在磁盤上呢?

redis提供了三種方式:save、bgsave、自動化

save

執行save命令,該命令會阻塞當前Redis服務器,執行save命令期間,Redis不能處理其他命令,直到RDB過程完成爲止。

執行完成時候如果存在老的RDB文件,就把新的替代掉舊的。我們的客戶端可能都是幾萬或者是幾十萬,這種方式顯然不可取。

bgsave

執行bgsave命令,執行該命令時,Redis會在後臺異步進行快照操作,快照同時還可以響應客戶端請求

具體操作是Redis進程執行fork操作創建子進程,RDB持久化過程由子進程負責,完成後自動結束。阻塞只發生在fork階段,一般時間很短。基本上 Redis 內部所有的RDB操作都是採用 bgsave 命令。

自動觸發

在redis.conf配置文件進行配置

比如“save m n”。表示m秒內數據集存在n次修改時,自動觸發bgsave。

附-其他一些相關配置:

**stop-writes-on-bgsave-error :**默認值爲yes。當啓用了RDB且最後一次後臺保存數據失敗,Redis是否停止接收數據。這會讓用戶意識到數據沒有正確持久化到磁盤上,否則沒有人會注意到災難(disaster)發生了。如果Redis重啓了,那麼又可以重新開始接收數據了

**rdbcompression ;**默認值是yes。對於存儲到磁盤中的快照,可以設置是否進行壓縮存儲。

**rdbchecksum :**默認值是yes。在存儲快照後,我們還可以讓redis使用CRC64算法來進行數據校驗,但是這樣做會增加大約10%的性能消耗,如果希望獲取到最大的性能提升,可以關閉此功能。

**dbfilename :**設置快照的文件名,默認是 dump.rdb

**dir:**設置快照文件的存放路徑,這個配置項一定是個目錄,而不能是文件名。

AOF機制

redis會將每一個收到的寫命令都通過write函數追加到AOF文件中。即日誌記錄。

當然,AOF文件會越來越大,所以redis會fork出一條新進程來將文件重寫。

在redis.conf文件中,AOF機制有以下幾種配置:

always:每次發生數據變更會被立即記錄到磁盤 性能較差但數據完整性比較好

everysec:每秒記錄,所以可能出現一秒數據丟失

正常來說,redis持久化是需要兩者加一起才更好。

redis線程模型

redis單線程模型中最爲核心的就是文件事件處理器

文件事件處理器包含:多個socket、IO多路複用程序、socket隊列、文件事件分派器、以及事件處理器

redis的單線程指的就是socket隊列,IO多路複用程序會把監聽有請求進來後,會將消息放入隊列中,文件分配器再從隊列中取出消息,然後再交給事件處理器。事件處理器會從對應的socket上讀取相關的數據,處理完成後,,產生AE_WRITABLE事件,並準備好準備好相應的響應數據,然後由對應的命令回覆處理器處理後返回對應的socket,返回給客戶端.

如下圖:

所以,通常所說的redis單線程是對應IO多路複用器及隊列而說的,其實redis還是多線程的,比如AOF和RDB,都是fork子線程完成,上圖事件處理器等,也是多線程完成。下文中提到的定時刪除過期key,也是多線程的

redis key過期策略

1、惰性刪除:當讀/寫一個已經過期的key時,會觸發惰性刪除策略,直接刪除掉這個過期key

2、主動刪除:Redis執行定時任務,刪除過期的key

3、當前已用內存超過限定內存時,會觸發主動清除

在 Redis 中, 常規操作由 redis.c/serverCron 實現, 它主要執行以下操作

  • 更新服務器的各類統計信息,比如時間、內存佔用、數據庫佔用情況等。
  • 清理數據庫中的過期鍵值對。
  • 對不合理的數據庫進行大小調整。
  • 關閉和清理連接失效的客戶端。
  • 嘗試進行 AOF 或 RDB 持久化操作。
  • 如果服務器是主節點的話,對附屬節點進行定期同步。
  • 如果處於集羣模式的話,對集羣進行定期同步和連接測試。

maxmemory 當前已用內存超過maxmemory限定時,觸發主動清理策略

配置如下:

  • volatile-lru:只對設置了過期時間的key進行LRU(默認值)
  • allkeys-lru : 刪除lru算法的key
  • volatile-random:隨機刪除即將過期key
  • allkeys-random:隨機刪除
  • volatile-ttl : 刪除即將過期的
  • noeviction : 永不過期,返回錯誤

redis集羣hash槽爲什麼是16383

我們知道,對於redis集羣,當客戶端請求key時,根據公式HASH_SLOT=CRC16(key) mod 16384,計算出映射到哪個分片上,然後Redis會去相應的節點進行操作!

CRC16算法產生的hash值有16bit,該算法可以產生2^16-=65536個值,那麼正常凱碩,redis的hash槽應該是65536。

以下爲作者的解答:

The reason is:

  • Normal heartbeat packets carry the full configuration of a node, that can be replaced in an idempotent way with the old in order to update an old config. This means they contain the slots configuration for a node, in raw form, that uses 2k of space with16k slots, but would use a prohibitive 8k of space using 65k slots.
  • At the same time it is unlikely that Redis Cluster would scale to more than 1000 mater nodes because of other design tradeoffs.

So 16k was in the right range to ensure enough slots per master with a max of 1000 maters, but a small enough number to propagate the slot configuration as a raw bitmap easily. Note that in small clusters the bitmap would be hard to compress because when N is small the bitmap would have slots/N bits set that is a large percentage of bits set.

https://github.com/antirez/redis/issues/2576

在redis集羣中,各個節點之間需要知道彼此的存在,他們之間定期都在發送ping/pong消息,交換數據信息。

數據結構如下:

我們看到有個myslots字段,發送節點負責的槽信息,長度爲[CLUSTER_SLOTS/8]

假設CLUSTER_SLOTS是65536,那麼myslots的長度爲:65536/8/1024=8K

假設CLUSTER_SLOTS是16384,那麼myslots的長度爲:16384/8/1024=2K

8K是2K的4倍,勢必浪費帶寬

定期發送消息的頻率:

  • (1)每秒會隨機選取5個節點,找出最久沒有通信的節點發送ping消息

  • (2)每100毫秒(1秒10次)都會掃描本地節點列表,如果發現節點最近一次接受pong消息的時間大於cluster-node-timeout/2 則立刻發送ping消息

    因此,每秒單節點發出ping消息數量爲 數量=1+10*num(node.pong_received>cluster_node_timeout/2)

redis節點個數:redis的集羣主節點數量基本不可能超過1000個,試想,沒如果redis節點超過1000個,在1000個節點裏面發送ping/pong消息,可能造成網絡擁堵。

那麼,對於節點數在1000以內的redis cluster集羣,16384個槽位夠用了。沒有必要拓展到65536個。

當然,還有壓縮比的考慮,redis所負責的哈希槽是通過一張bitmap的形式來保存的,在傳輸過程中,會對bitmap進行壓縮,但是如果bitmap的填充率slots / N很高的話(N表示節點數),bitmap的壓縮率就很低。 如果節點數很少,而哈希槽數量很多的話,bitmap的壓縮率就很低。

綜上所述,作者決定取16384個槽,不多不少,剛剛好!

redis爲什麼使用hash槽,而不使用一致性hash

一致性hash實際上是將整個hash值空間組織成一個圓環,hash值空間有2的32次方-1個數,並通過虛擬節點解決圓環分配不均勻的問題;

一致性hash有點:

1、擴展性:當擴展一個節點時,隻影響順時針遇到的數據;

2、容錯性:當節點故障時,隻影響逆時針遇到的數據

缺點:當增刪節點時,需要找出影響的節點;

這中情況下,redis只能當緩存,不能當數據庫使用;

所以redis使用hash槽,

當增刪節點時,我們可以精準的知道影響的槽位,並進行數據遷移。

集羣進入fail狀態的必要條件

A、某個主節點和所有從節點全部掛掉,我們集羣就進入faill狀態。

B、如果集羣超過半數以上master掛掉,無論是否有slave,集羣進入fail狀態.

C、如果集羣任意master掛掉,且當前master沒有slave.集羣進入fail狀態

投票過程是集羣中所有master參與,如果半數以上master節點與master節點通信超時(cluster-node-timeout),認爲當前master節點掛掉。

​ 選舉的依據依次是:網絡連接正常->5秒內回覆過INFO命令->10*down-after-milliseconds內與主連接過的->從服務器優先級->複製偏移量->運行id較小的。選出之後通過slaveif no ont將該從服務器升爲新主服務器。

​ 通過slaveof ip port命令讓其他從服務器複製該信主服務器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章