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中所有的鍵值對
OK
127.0.0.1:6379> dbsize # 查看Redis中鍵值對數量
(integer) 0
127.0.0.1:6379> info memory # 查看Redis占用的內存數為834.26K
# Memory
used_memory:854280
used_memory_human:834.26K
used_memory_rss:5931008
used_memory_rss_human:5.66M
used_memory_peak:854280
used_memory_peak_human:834.26K
total_system_memory:2080903168
total_system_memory_human:1.94G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:6.94
mem_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內存容量
# Memory
used_memory:1347976664
used_memory_human:1.26G
used_memory_rss:1381294080
used_memory_rss_human:1.29G
used_memory_peak:1347976664
used_memory_peak_human:1.26G
total_system_memory:2080903168
total_system_memory_human:1.94G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:1.02
mem_allocator:jemalloc-3.6.0
127.0.0.1:6379> dbsize # 查看Redis中數據總數
(integer) 4999999
8.在系統命令提示符中查看生成的RDB文件
[root@mysql redis]# ls -lah # Redis的RDB文件經過壓縮后的大小為122MB
total 122M
drwxr-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]# ll
total 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) 4999998
127.0.0.1:6379> info memory # 查看Redis占用的內存數
# Memory
used_memory:1347973736
used_memory_human:1.26G # Redis占用了1.2G內存
used_memory_rss:1383464960
used_memory_rss_human:1.29G
used_memory_peak:1348224368
used_memory_peak_human:1.26G
total_system_memory:2080903168
total_system_memory_human:1.94G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:1.03
mem_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 1
save 300 10
save 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 1
save 300 10
save 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中已有的所有數據
OK
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> set c d
OK
127.0.0.1:6379> set e f
OK
127.0.0.1:6379> set g i
OK
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.10
3325: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 6379
3325: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 3349
3349:C 13 Oct 15:55:17.434 * DB saved on disk
3349:C 13 Oct 15:55:17.435 * RDB: 2 MB of memory used by copy-on-write
3325: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 -lah
total 8.0K
drwxr-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之間進行一定的權衡,通過優先從性能方面進行考慮,設置為yes
auto-aof-rewrite-percentage 100 # 觸發重寫所需的AOF文件體積增長率
auto-aof-rewrite-min-size 64mb # 觸發重寫所需的AOF文件大小
3.2.10 Redis的AOF功能示例
1.打開一個Redis客戶端
[root@mysql ~]# redis-cli
127.0.0.1:6379> config get appendonly # 查看appendonly配置項結果
1) "appendonly"
2) "no"
127.0.0.1:6379> config set appendonly yes # 設置appendonly選項為yes
OK
127.0.0.1:6379> config rewrite # 把配置寫入到文件中
OK
127.0.0.1:6379> config get appendonly # 再次查看appendonly選項結果,看修改是否寫入到配置文件中
1) "appendonly"
2) "yes"
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> set hello php
OK
127.0.0.1:6379> set hello python
OK
127.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文件大小為278B
total 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 -h
total 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
# Persistence
loading:0
rdb_changes_since_last_save:1
rdb_bgsave_in_progress:0
rdb_last_save_time:1539409132
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_delayed_fsync:100 # AOF被阻塞的歷史次數,無法看到某次AOF被阻塞的時間點
單機多實例部署中持久化的優化可以參考硬盤開銷的優化策略
5.Redis持久化總結
RDB是Redis內存數據到硬盤的快照,用於持久化
save通常會阻塞Redis
BGSAVE不會阻塞Redis,但是fork新進程會阻塞Redis
SAVE自動配置滿足任一條件就會被執行