高可用Redis(七):Redis持久化

pswyjz發表於2021-09-09

1.什麼是持久化

持久化就是將資料從掉電易失的記憶體同步到能夠永久儲存的裝置上的過程

2.Redis為什麼需要持久化

redis將資料儲存在記憶體中,一旦Redis伺服器被關閉,或者執行Redis服務的主機本身被關閉的話,儲存在記憶體裡面的資料就會丟失

如果僅僅將redis用作快取的話,那麼這種資料丟失帶來的問題並不是非常大,只需要重啟機器,然後再次將資料同步到快取中就可以了

但如果將redis用作資料庫的話,那麼因為一些原因導致資料丟失的情況就不能接受

Redis的持久化就是將儲存在記憶體裡面的資料以檔案形式儲存硬碟裡面,這樣即使Redis服務端被關閉,已經同步到硬碟裡面的資料也不會丟失

除此之外,持久化也可以使Redis伺服器重啟時,透過載入同步的持久檔案來還原之前的資料,或者使用持久化檔案來進行資料備份和資料遷移等工作

3.Redis持久化方式

3.1 RDB(Redis DB)方式

RDB持久化功能可以將Redis中所有資料生成快照並以二進行檔案的形式儲存到硬碟裡,檔名為.RDB檔案

在Redis啟動時載入RDB檔案,Redis讀取RDB檔案內容,還原伺服器原有的資料庫資料

過程如下圖所示:

圖片描述

Redis服務端建立RDB檔案,有三種方式

3.1.1 使用SAVE命令手動同步建立RDB檔案

客戶端向Redis服務端傳送SAVE命令,服務端把當前所有的資料同步儲存為一個RDB檔案

透過向伺服器傳送SAVE命令,Redis會建立一個新的RDB檔案

在執行SAVE命令的過程中(也就是即時建立RDB檔案的過程中),Redis服務端將被阻塞,無法處理客戶端傳送的其他命令請求

只有在SAVE命令執行完畢之後(也就時RDB檔案建立完成之後),伺服器才會重新開始處理客戶端傳送的命令請求

如果已經存在RDB檔案,那麼伺服器將自動使用新的RDB檔案去代替舊的RDB檔案

例子:

1.修改Redis的配置檔案/etc/redis.conf,把下面三行註釋掉

#save 900 1#save 300 10#save 60 10000

2.執行下面三條命令

127.0.0.1:6379> flushall                # 清空Redis中所有的鍵值對OK127.0.0.1:6379> dbsize                  # 檢視Redis中鍵值對數量
(integer) 0
127.0.0.1:6379> info memory             # 檢視Redis佔用的記憶體數為834.26K# Memoryused_memory:854280used_memory_human:834.26Kused_memory_rss:5931008used_memory_rss_human:5.66Mused_memory_peak:854280used_memory_peak_human:834.26Ktotal_system_memory:2080903168total_system_memory_human:1.94Gused_memory_lua:37888used_memory_lua_human:37.00Kmaxmemory:0maxmemory_human:0Bmaxmemory_policy:noevictionmem_fragmentation_ratio:6.94mem_allocator:jemalloc-3.6.0

3.從Redis的配置檔案可以知道,Redis的RDB檔案儲存在/var/lib/redis/目錄中

[root@mysql redis]# pwd/var/lib/redis
[root@mysql redis]# ll      # 檢視Redis的RDB目錄下的檔案total 0

4.執行python指令碼,向Redis中插入500萬條資料

import redis

client = redis.StrictRedis(host='192.168.81.101',port=6379)for i in range(5000000):
    client.sadd('key' + str(i),'value'+ str(i))

5.向Redis中寫入500萬條資料完成後,執行SAVE命令

127.0.0.1:6379> save        # 執行SAVE命令,花費5.72秒OK(5.72s)

6.切換另一個Redis-cli視窗執行命令

127.0.0.1:6379> spop key1   # 執行spop命令彈出'key1'的值,因為SAVE命令在執行的原因,spop命令會阻塞直到save命令執行完成,執行spop命令共花費4.36秒"value1"(4.36s)

7.檢視Redis佔用的記憶體數

127.0.0.1:6379> info memory     # 向Redis中寫入500萬條資料後,Redis佔用1.26G記憶體容量
# Memoryused_memory:1347976664used_memory_human:1.26Gused_memory_rss:1381294080used_memory_rss_human:1.29Gused_memory_peak:1347976664used_memory_peak_human:1.26Gtotal_system_memory:2080903168total_system_memory_human:1.94Gused_memory_lua:37888used_memory_lua_human:37.00Kmaxmemory:0maxmemory_human:0Bmaxmemory_policy:noevictionmem_fragmentation_ratio:1.02mem_allocator:jemalloc-3.6.0127.0.0.1:6379> dbsize          # 檢視Redis中資料總數
(integer) 4999999

8.在系統命令提示符中檢視生成的RDB檔案

[root@mysql redis]# ls -lah         # Redis的RDB檔案經過壓縮後的大小為122MBtotal 122Mdrwxr-x---   2 redis redis   22 Oct 13 15:31 .drwxr-xr-x. 64 root  root  4.0K Oct 13 13:38 ..-rw-r--r--   1 redis redis 122M Oct 13 15:31 dump.rdb

SAVE命令的時間複雜度為O(N)

3.1.2 使用BGSAVE命令非同步建立RDB檔案

執行BGSAVE命令也會建立一個新的RDB檔案

BGSAVE不會造成redis伺服器阻塞:在執行BGSAVE命令的過程中,Redis服務端仍然可以正常的處理其他的命令請求

BGSAVE命令執行步驟:

1.Redis服務端接受到BGSAVE命令
2.Redis服務端透過fork()來生成一個名叫redis-rdb-bgsave的程式,由redis-rdb-bgsave子程式來建立RDB檔案,而Redis主程式則繼續處理客戶端的命令請求
3.當redis-rdb-bgsave子程式建立完成RDB檔案,會向Redis主程式傳送一個訊號,告知Redis主程式RDB檔案已經建立完畢,然後redis-rdb-bgsave子程式退出
4.Redis伺服器(父程式)接手子程式建立的RDB檔案,BGSAVE命令執行完畢

BGSAVE命令執行過程如下圖所示

圖片描述

Redis主程式因為建立子程式,會消耗額外的記憶體
需要注意的是:如果在Redis主程式fork子程式的過程中花費的時間過多,Redis仍然可能會阻塞

BGSAVE是一個非同步命令,Redis客戶端向Redis服務端傳送BGSAVE命令後會立即得到回覆,而實際的操作在Redis服務端回覆之後才開始

例子:

現在Redis中已經有500萬條資料

1.刪除Redis的RDB檔案

[root@mysql redis]# rm -rf *.rdb    # 刪除Redis的舊的RDB檔案[root@mysql redis]# lltotal 0

2.在redis-cli中執行BGSAVE命令

127.0.0.1:6379> bgsave              # 執行BGSAVE命令後會立即得到響應Background saving started

3.在另一個redis-cli中執行命令

127.0.0.1:6379> spop key2           # 從Redis的集合中彈出'key2'的值"value2"127.0.0.1:6379> dbsize              # 檢視Redis中所有資料的總數(integer) 4999998127.0.0.1:6379> info memory         # 檢視Redis佔用的記憶體數# Memoryused_memory:1347973736used_memory_human:1.26G             # Redis佔用了1.2G記憶體used_memory_rss:1383464960used_memory_rss_human:1.29Gused_memory_peak:1348224368used_memory_peak_human:1.26Gtotal_system_memory:2080903168total_system_memory_human:1.94Gused_memory_lua:37888used_memory_lua_human:37.00Kmaxmemory:0maxmemory_human:0Bmaxmemory_policy:noevictionmem_fragmentation_ratio:1.03mem_allocator:jemalloc-3.6.0

4.在系統命令提示符中執行命令

[root@mysql redis]# ps aux | grep redis | grep -v 'redis-cli'redis       856  3.1 66.3 1486404 1348920 ?     Ssl  13:38   3:39 /usr/bin/redis-server 0.0.0.0:6379      # Redis的主程式root       3015  0.0  0.0 112664   968 pts/3    R+   15:36   0:00 grep --color=auto redis
[root@mysql redis]# ps aux | grep redis | grep -v 'redis-cli'redis       856  3.0 66.3 1486404 1348956 ?     Ssl  13:38   3:39 /usr/bin/redis-server 0.0.0.0:6379      # Redis的主程式redis      3026 87.1 66.3 1486408 1348032 ?     R    15:36   0:05 redis-rdb-bgsave 0.0.0.0:6379           # Redis主程式fork的子程式root       3028  0.0  0.0 112664   968 pts/3    R+   15:37   0:00 grep --color=auto redis
[root@mysql redis]# pwd/var/lib/redis
[root@mysql redis]# ls -lah             # 檢視BGSAVE命令執行後得到的RDB檔案total 122M
drwxr-x---   2 redis redis   22 Oct 13 15:37 .
drwxr-xr-x. 64 root  root  4.0K Oct 13 13:38 ..
-rw-r--r--   1 redis redis 122M Oct 13 15:37 dump.rdb

BGSAVE命令的時間複雜度為O(N)

SAVE命令與BGSAVE命令的區別

圖片描述

RDB持久化方式的總結

SAVE建立RDB檔案的速度會比BGSAVE快,SAVE可以集中資源來建立RDB檔案
如果資料庫正在上線當中,就要使用BGSAVE
如果資料庫需要維護,可以使用SAVE命令

3.1.3 自動建立RDB檔案

開啟Redis的配置檔案/etc/redis.conf

save 900 1save 300 10save 60 10000

自動持久化配置解釋:

save 900 1表示:如果距離上一次建立RDB檔案已經過去的900秒時間內,Redis中的資料發生了1次改動,則自動執行BGSAVE命令
save 300 10表示:如果距離上一次建立RDB檔案已經過去的300秒時間內,Redis中的資料發生了10次改動,則自動執行BGSAVE命令
save 60 10000表示:如果距離上一次建立RDB檔案已經過去了60秒時間內,Redis中的資料發生了10000次改動,則自動執行BGSAVE命令

當三個條件中的任意一個條件被滿足時,Redis就會自動執行BGSAVE命令

需要注意的是:每次執行BGSAVE命令建立RDB檔案之後,伺服器為實現自動持久化而設定的時間計數器和次數計數器就會被清零,並重新開始計數,所以多個儲存條件的效果是不會疊加

使用者也可以透過設定多個SAVE選項來設定自動儲存條件,

Redis關於自動持久化的配置

rdbcompression yes              建立RDB檔案時,是否啟用壓縮
stop-writes-on-bgsave-error yes 執行BGSAVE命令時發生錯誤是否停止寫入
rdbchecksum yes                 是否對生成RDB檔案進行檢驗
dbfilename dump.rdb             持久化生成的備份檔案的名字
dir /var/lib/redis/6379         RDB檔案儲存的目錄

例子:

1.修改Redis配置檔案/etc/redis.conf

save 900 1save 300 10save 60 5

2.重啟Redis,刪除已經建立的RDB檔案

[root@mysql redis]# systemctl restart redis[root@mysql redis]# rm -rf dump.rdb [root@mysql redis]# pwd/var/lib/redis
[root@mysql redis]# ls[root@mysql redis]#

3.開啟redis-cli,向Redis中設定5個value

127.0.0.1:6379> flushall            # 清空Redis中已有的所有資料OK127.0.0.1:6379> set hello worldOK127.0.0.1:6379> set a b OK127.0.0.1:6379> set c dOK127.0.0.1:6379> set e fOK127.0.0.1:6379> set g iOK

4.檢視Redis的日誌記錄

[root@mysql ~]# tail /var/log/redis/redis.log           # 檢視Redis日誌的最後10行3325:M 13 Oct 15:53:44.323 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.3325:M 13 Oct 15:53:44.323 # Server started, Redis version 3.2.103325:M 13 Oct 15:53:44.323 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.3325:M 13 Oct 15:53:44.323 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.3325:M 13 Oct 15:53:44.323 * The server is now ready to accept connections on port 63793325:M 13 Oct 15:55:17.431 * 5 changes in 60 seconds. Saving...3325:M 13 Oct 15:55:17.432 * Background saving started by pid 33493349:C 13 Oct 15:55:17.434 * DB saved on disk3349:C 13 Oct 15:55:17.435 * RDB: 2 MB of memory used by copy-on-write3325:M 13 Oct 15:55:17.534 * Background saving terminated with success

5.檢視Redis的持久化檔案

[root@mysql redis]# pwd/var/lib/redis[root@mysql redis]# ls -lahtotal 8.0Kdrwxr-x---   2 redis redis   22 Oct 13 15:55 .drwxr-xr-x. 64 root  root  4.0K Oct 13 13:38 ..-rw-r--r--   1 redis redis  115 Oct 13 15:55 dump.rdb

RDB現存問題

  • 1.耗時耗效能

    Redis把記憶體中的資料dump到硬碟中生成RDB檔案,首先要把所有的資料都進行持久化,所需要的時間複雜度為O(N),同時把資料dump到檔案中,也需要消耗CPU資源,
    由於BGSAVE命令有一個fork子程式的過程,雖然不是完整的記憶體複製,而是基於copy-on-write的策略,但是如果Redis中的資料非常多,佔用的記憶體頁也會非常大,fork子程式時消耗的記憶體資源也會很多
    磁碟IO效能的消耗,生成RDB檔案本來就是把記憶體中的資料儲存到硬碟當中,如果生成的RDB檔案非常大,儲存到硬碟的過程中消耗非常多的硬碟IO

  • 2.不可控,丟失資料

    自動建立RDB檔案的過程中,在上一次建立RDB檔案以後,又向Redis中寫入多條資料,如果此時Redis服務停止,則從上一次建立RDB檔案到Redis服務掛機這個時間段內的資料就丟失了

    3.2 AOF(AppendOnlyFile)方式

    3.2.1 AOF原理

    AOF建立

當向Redis中寫入一條資料時,就向AOF檔案中新增一條寫記錄

如下圖所示

圖片描述

AOF恢復

Redis發生當機重啟後,讀取AOF檔案對Redis中的資料進行完整的恢復,而且使用AOF檔案來恢復Redis中的資料是實時的

如下圖所示

圖片描述

3.2.2 AOF

AOF持久化儲存資料庫的方法是:每當有修改的資料庫的命令被執行時,伺服器就會將執行的命令寫入到AOF檔案的末尾。

因為AOF檔案裡面儲存了伺服器執行過的所有資料庫修改的命令,所以Redis只要重新執行一遍AOF檔案裡面儲存的命令,就可以達到還原資料庫的目的

3.2.3 AOF安全性問題

雖然伺服器執行一次修改資料庫的命令,執行的命令就會被寫入到AOF檔案,但這並不意味著AOF持久化方式不會丟失任何資料

在linux系統中,系統呼叫write函式,將一些資料儲存到某檔案時,為了提高效率,系統通常不會直接將內容寫入硬碟裡面,而是先把資料儲存到硬碟的緩衝區之中。

等到緩衝區被填滿,或者使用者執行fsync呼叫和fdatasync呼叫時,作業系統才會將儲存在緩衝區裡的內容真正的寫入到硬碟裡

對於AOF持久化來說,當一條命令真正的被寫入到硬碟時,這條命令才不會因為停機而意外丟失

因此,AOF持久化在遭遇停機時丟失命令的數量,取決於命令被寫入硬碟的時間

越早將命令寫入到硬碟,發生意外停機時丟失的資料就越少,而越遲將命令寫入硬碟,發生意外停機時丟失的資料就越多

3.2.4 AOF三種策略

為了控制Redis伺服器在遇到意外停機時丟失的資料量,Redis為AOF持久化提供了appendfsync選項,這個選項的值可以是always,everysec或者no

Always

Redis每寫入一個命令,always會把每條命令都重新整理到硬碟的緩衝區當中然後將緩衝區裡的資料寫入到硬碟裡。
這種模式下,Redis即使用遭遇意外停機,也不會丟失任何自己已經成功執行的資料

Everysec

Redis每一秒呼叫一次fdatasync,將緩衝區裡的命令寫入到硬碟裡,
這種模式下,當Redis的資料交換很多的時候可以保護硬碟
即使Redis遭遇意外停機時,最多隻丟失一秒鐘內的執行的資料

No

伺服器不主動呼叫fdatasync,由作業系統決定任何將緩衝區裡面的命令寫入到硬碟裡,這種模式下,伺服器遭遇意外停機時,丟失的命令的數量是不確定的

3.2.5 AOF三種方式比較

執行速度:always的速度慢,everysec和no都很快always不丟失資料,但是硬碟IO開銷很多,一般的SATA硬碟一秒種只能寫入幾百次資料
everysec每秒同步一次資料,如果Redis發生故障,可能會丟失1秒鐘的資料
no則系統控制,不可控,不知道會丟失多少資料

3.2.6 AOF重寫功能簡介

隨著伺服器的不斷執行,為了記錄Redis中資料的變化,Redis會將越來越多的命令寫入到AOF檔案中,使得AOF檔案的體積來斷增大
為了讓AOF檔案的大小控制在合理的範圍,redis提供了AOF重寫功能,透過這個功能,伺服器可以產生一個新的AOF檔案:

新的AOF檔案記錄的資料庫資料和原有AOF檔案記錄的資料庫資料完全一樣
新的AOF檔案會使用盡可能少的命令來記錄資料庫資料,因此新的AOF檔案的體積通常會比原有AOF檔案的體積要小得多
AOF重寫期間,伺服器不會被阻塞,可以正常處理客戶端傳送的命令請求

圖片描述

AOF重寫功能就是把Redis中過期的,不再使用的,重複的以及一些可以最佳化的命令進行最佳化,重新生成一個新的AOF檔案,從而達到減少硬碟佔用量和加速Redis恢復速度的目的

3.2 7 AOF重寫觸發方式

1.向Redis傳送BGREWRITEAOF命令

類似於BGSAVE命令,Redis主程式會fork一個子程式,由子程式去完成AOF重寫

圖片描述

這裡的AOF重寫是將Redis記憶體中的資料進行一次回溯,得到一個AOF檔案,而不是將已有的AOF檔案重寫成一個新的AOF檔案

2.透過配置選項自動執行BGREWRITEAOF命令

auto-aof-rewrite-min-size

觸發AOF重寫所需的最小體積:只要在AOF檔案的大小超過設定的size時,Redis會進行AOF重寫,這個選項用於避免對體積過小的AOF檔案進行重寫

auto-aof-rewrite-percentage

指定觸發重寫所需的AOF檔案體積百分比:當AOF檔案的體積大於auto-aof-rewrite-min-size指定的體積,並且超過上一次重寫之後的AOF檔案體積的percent%時,就會觸發AOF重寫,如果伺服器剛啟動不久,還沒有進行過AOF重寫,那麼使用伺服器啟動時載入的AOF檔案的體積來作為基準值。   將這個值設定為0表示關閉自動AOF重寫功能

只有當上面兩個條件同時滿足時才會觸發Redis的AOF重寫功能

3.2.8 AOF重寫的流程

圖片描述

如上圖所示:

1.無論是執行bgrewriteaof命令還是自動進行AOF重寫,實際上都是執行BGREWRITEAOF命令2.執行bgrewriteaof命令,Redis會fork一個子程式,3.子程式對記憶體中的Redis資料進行回溯,生成新的AOF檔案4.Redis主程式會處理正常的命令操作5.同時Redis把會新的命令寫入到aof_rewrite_buf當中,當bgrewriteaof命令執行完成,新的AOF檔案生成完畢,Redis主程式會把aof_rewrite_buf中的命令追加到新的AOF檔案中6.用新生成的AOF檔案替換舊的AOF檔案

3.2.9 配置檔案中AOF相關選項

appendonly   no                     # 改為yes,開啟AOF功能appendfilename  "appendonly.aof"    # 生成的AOF的檔名appendfsync everysec                # AOF同步的策略no-appendfsync-on-rewrite no        # AOF重寫時,是否做append的操作
    AOF重寫非常消耗伺服器的效能,子程式要將記憶體中的資料刷到硬碟中,肯定會消耗硬碟的IO
    而正常的AOF也要將記憶體中的資料寫入到硬碟當中,此時會有一定的衝突
    因為rewrite的過程在資料量比較大的時候,會佔用大量的硬碟的IO
    在AOF重寫後,生成的新的AOF檔案是完整且安全的資料
    如果AOF重寫失敗,如果設定為no則正常的AOF檔案中會丟失一部分資料
    生產環境中會在yes和no之間進行一定的權衡,透過優先從效能方面進行考慮,設定為yesauto-aof-rewrite-percentage 100     # 觸發重寫所需的AOF檔案體積增長率auto-aof-rewrite-min-size 64mb      # 觸發重寫所需的AOF檔案大小

3.2.10 Redis的AOF功能示例

1.開啟一個Redis客戶端

[root@mysql ~]# redis-cli127.0.0.1:6379> config get appendonly           # 檢視appendonly配置項結果
1) "appendonly"
2) "no"
127.0.0.1:6379> config set appendonly yes       # 設定appendonly選項為yesOK      127.0.0.1:6379> config rewrite                  # 把配置寫入到檔案中OK127.0.0.1:6379> config get appendonly           # 再次檢視appendonly選項結果,看修改是否寫入到配置檔案中
1) "appendonly"
2) "yes"
127.0.0.1:6379> set hello worldOK127.0.0.1:6379> set hello phpOK127.0.0.1:6379> set hello pythonOK127.0.0.1:6379> incr counter(integer) 1
127.0.0.1:6379> incr counter(integer) 2
127.0.0.1:6379> rpush list a(integer) 1
127.0.0.1:6379> rpush list b(integer) 2
127.0.0.1:6379> rpush list c(integer) 3
127.0.0.1:6379> dbsize                          # 檢視Redis中資料量
(integer) 3

2.在系統命令提示符中檢視AOF檔案的大小

[root@mysql redis]# ll -h                       # 正常的AOF檔案大小為278Btotal 8.0K-rw-r--r-- 1 redis redis 278 Oct 13 18:32 appendonly.aof-rw-r--r-- 1 redis redis 135 Oct 13 18:32 dump.rdb

3.在Redis客戶端執行AOF重寫命令

127.0.0.1:6379> bgrewriteaof                    # 在Redis客戶端中執行AOF重寫命令Background append only file rewriting started

4.再次在系統命令提示符中檢視新的AOF檔案大小

[root@mysql redis]# ll -htotal 8.0K-rw-r--r-- 1 redis redis 138 Oct 13 18:33 appendonly.aof        # AOF重寫後檔案大小為138B-rw-r--r-- 1 redis redis 135 Oct 13 18:32 dump.rdb[root@mysql redis]#

3.3 RDB和AOF的選擇

3.3.1 RDB和AOF的比較

圖片描述

3.3.2 RDB最佳策略

  • RDB是一個重操作

  • Redis主從複製中的全量複製是需要主節點執行一次BGSAVE命令,然後把RDB檔案同 步給從Redis從節點來實現複製的效果

  • 如果對Redis按小時或者按天這種比較大的量級進行備份,使用RDB是一個不錯的選擇,集中備份管理比較方便

  • 在Redis主從架構中,可以在Redis從節點開啟RDB,可以在本機儲存RDB的歷史檔案,但是生成RDB檔案的週期不要太頻繁

  • Redis的單機多部署模式對伺服器的CPU,記憶體,硬碟有較大開銷,實際生產環境根據需要進行設定

    3.3.3 AOF最佳策略

  • 建議把appendfsync選項設定為everysec,進行持久化,這種情況下Redis當機最多隻會丟失一秒鐘的資料

  • 如果使用Redis做為快取時,即使資料丟失也不會造成任何影響,只需要在下次載入時重新從資料來源載入就可以了

  • Redis單機多部署模式下,AOF集中操作時會fork大量的子程式,可能會出現記憶體爆滿或者導致作業系統使用SWAP分割槽的情況

  • 一般分配伺服器60%到70%的記憶體給Redis使用,剩餘的記憶體分留給類似fork的操作

    3.3.4 RDB和AOF的最佳使用策略

  • 使用max_memory對Redis進行規劃,例如Redis使用單機多部署模式時,每個Redis可用記憶體設定為4G,這樣無論是使用RDB模式還是AOF模式進行持久化,fork子程式操作都只需要較小的開銷

  • Redis分散式時,小分片會產生更多的程式,可能會對CPU的消耗更大

  • 根據快取或者儲存不同架構使用不同策略

  • 使用監控軟體對伺服器的硬碟,記憶體,負載,網路進行監控,以對伺服器各硬碟有更全面的瞭解,方便發生故障時進行定位

  • 不要佔用100%的記憶體

    上面的策略,無論使用RDB還是使用AOF,都可以做為參考

    4. Redis持久化開發運維問題

    4.1 Redis的fork操作

  • Redis的fork操作是同步操作

  • 執行BGSAVE和BGAOF命令時,實際上都是先執行fork操作,fork操作只是記憶體頁的複製,而不是完全對記憶體的複製

  • fork操作在大部分情況下是非常快的,但是如果fork操作被阻塞,也會阻塞Redis主執行緒的執行

  • fork與記憶體量息息相關:Redis中資料佔用的記憶體越大,耗時越長(與機器型別有關),可以透過info memory命令檢視上次fork操作消耗的微秒數:latest_fork_usec:0

  • fork操作最佳化:

      1.優先使用物理機或者高效支援fork操作的虛擬化技術  2.控制Redis例項最大可用記憶體:maxmemory  3.合理配置linux記憶體分配策略:vm.overcommit_memory = 1
      4.降低fork頻率,例如放寬AOF重寫自動觸發機制,不必要的全量複製

    4.2 程式外開銷

    4.2.1 CPU開銷

RDB和AOF檔案的生成操作都屬於CPU密集型

通常子程式的開銷會佔用90%以上的CPU,檔案寫入是非常密集的過程

CPU開銷最佳化

  • 1.不做CPU繫結,不要把Redis程式繫結在一顆CPU上,這樣Redis fork子程式時,會分散消耗的CPU資源,不會對Redis主程式造成影響

  • 2.不和CPU密集型應用在一臺伺服器上部署,這樣不會產生CPU資源的過度競爭

  • 3.在使用單機部署Redis時,不要發生大量的RDB,BGSAVE,AOF的過程,保證可以節省一定的CPU資源

    4.2.2 記憶體開銷

fork子程式會產生一定記憶體開銷,理論上fork子程式操作佔用的記憶體是等於父程式佔用的記憶體

在linux系統中,有一種顯式複製的機制:copy-on-write,父子程式會共享相同的實體記憶體頁,當父程式有寫請求的時候,會建立一個父本,此時才會消耗一定的記憶體。在這個過程中,子程式會共享fork時,父程式的記憶體的快照

Redis在做BGSAVE或者AOF操作fork產生子程式的過程中,如果父程式的記憶體頁有大量的寫入操作時子程式的記憶體開銷會非常大,因為子程式會做一個父本

如果父程式沒有多少寫入操作時,fork操作不會佔用過多的記憶體資源,可以在Redis的日誌中看到

記憶體開銷最佳化:

  • 1.在單機部署Redis時,不要產生大量的重寫,這樣記憶體開銷也會比較小

  • 2.儘量主程式寫入量比較小時,執行BGSAVE或者AOF操作

  • 3.linux系統最佳化:

    echo never > /sys/kernel/mm/transparent_hugepage/enabled

4.2.3 硬碟開銷

AOF和RDB檔案的寫入,會佔用硬碟的IO及容量,可以使用iostat命令和iotop命令檢視分析

硬碟開銷最佳化:

  • 1.不要和硬碟高負載服務部署在一起,如儲存服務,訊息佇列等

  • 2.修改Redis配置檔案:在AOF重寫期間不要執行AOF操作,以減少記憶體開銷

              no-appendfsync-on-rewrite = yes
  • 3.根據硬碟寫入量決定磁碟型別:例如使用SSD

  • 4.單機多部署模式持久化時,檔案目錄可以考慮分盤。即對不同的Redis例項以埠來進行區分,持久化檔案也以埠來區分

4.3 AOF追加阻塞

圖片描述

AOF一般都是一秒中執行一次

AOF追加阻塞流程

1.主執行緒負責寫入AOF緩衝區
2.AOF同步執行緒每秒鐘執行一次同步硬碟操作,同時還會記錄一次最近一次的同步時間
3.主執行緒會對比上次AOF同步時間,如果距離上次同步時間在2秒之內,則返回主執行緒
4.如果距離上次AOF同步時間超過2秒,則主執行緒會阻塞,直到同步完成

AOF追加阻塞是保證AOF檔案安全性的一種策略

為了達到每秒刷盤的效果,主執行緒會阻塞直到同步完成

這樣就會產生兩個問題:

因為主執行緒是在負責Redis日常命令的處理,所以Redis主執行緒不能阻塞,而此時Redis的主執行緒被阻塞
如果AOF追加被阻塞,每秒刷盤的策略並不會每秒都執行,可能會丟失2秒的資料

AOF阻塞定位:

如果AOF追加被阻塞,可以透過命令檢視:

127.0.0.1:6379> info persistence# Persistenceloading:0rdb_changes_since_last_save:1rdb_bgsave_in_progress:0rdb_last_save_time:1539409132rdb_last_bgsave_status:okrdb_last_bgsave_time_sec:-1rdb_current_bgsave_time_sec:-1aof_enabled:0aof_rewrite_in_progress:0aof_rewrite_scheduled:0aof_last_rewrite_time_sec:-1aof_current_rewrite_time_sec:-1aof_last_bgrewrite_status:okaof_last_write_status:okaof_delayed_fsync:100               # AOF被阻塞的歷史次數,無法看到某次AOF被阻塞的時間點

單機多例項部署中持久化的最佳化可以參考硬碟開銷的最佳化策略

5.Redis持久化總結

RDB是Redis記憶體資料到硬碟的快照,用於持久化
save通常會阻塞Redis
BGSAVE不會阻塞Redis,但是fork新程式會阻塞Redis
SAVE自動配置滿足任一條件就會被執行

原文出處:https://www.cnblogs.com/renpingsheng/p/9786806.html

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/755/viewspace-2815287/,如需轉載,請註明出處,否則將追究法律責任。

相關文章