redis持久化和常見故障

jopen 10年前發布 | 58K 次閱讀 Redis NoSQL數據庫

redis 主從復制

Redis主從復制的原理

當建立主從關系時,slave配置slaveof <master_host> <master_port> 。slave服務器會向主服務器發送一個sync命令。master接受并fork一個進程來執行BGSAVE命令。該命令生成一個RDB文件并且全量發送給slave服務器,slave服務器接收并載入RDB文件,同時,主服務器將緩沖區的命令以增量的方式發送給從服務器,最終使從服務器的數據狀態和主服務器保持一致。

RDB的工作原理

當redis生成dump.rdb文件時,工作過程如下

  • redis主進程fork一個子進程

  • fork出來的子進程將內存的數據集dump到臨時的RDB中

  • 當子進程對臨時的RDB文件寫入完畢,redis用新的RDB文件代替舊的RDB文件

AOF的工作原理

AOF :append only file。每當Redis執行一個改變數據集的命令時,這個命令都會被追加到AOF文件的末尾。當redis重新啟動時,程序可以通過AOF文件恢復數據

持久化文件監控

Redis 監控最直接的方法當然就是使用系統提供的 info 命令來做了,只需要執行下面一條命令,就能獲得 Redis 系統的狀態報告。

redis-cli info

RDB文件狀態監控

其中跟RDB文件狀態監控相關的參數

  • rdb_changes_since_last_save 表明上次RDB保存以后改變的key次數

  • rdb_bgsave_in_progress 表示當前是否在進行bgsave操作。是為1

  • rdb_last_save_time 上次保存RDB文件的時間戳

  • rdb_last_bgsave_time_sec 上次保存的耗時

  • rdb_last_bgsave_status 上次保存的狀態

  • rdb_current_bgsave_time_sec 目前保存RDB文件已花費的時間

AOF文件狀態監控

其中跟AOF文件狀態監控相關的參數

  • aof_enabled AOF文件是否啟用

  • aof_rewrite_in_progress 表示當前是否在進行寫入AOF文件操作

  • aof_rewrite_scheduled

  • aof_last_rewrite_time_sec 上次寫入的時間戳

  • aof_current_rewrite_time_sec:-1

  • aof_last_bgrewrite_status:ok 上次寫入狀態

  • aof_last_write_status:ok 上次寫入狀態

查看rdb文件生成耗時

在我們優化master之前,可以看看目前我們的其中一個生產環境的的redis的持久化狀態

# Persistence
loading:0
rdb_changes_since_last_save:116200
rdb_bgsave_in_progress:1
rdb_last_save_time:1448944451
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:85
rdb_current_bgsave_time_sec:33
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

通過redis-cli的info命令,可以看到 「rdb_last_bgsave_time_sec」參數的值,
這個值表示上次bgsave命令執行的時間。在磁盤IO定量的情況下,redis占用的內存越大,
這個值也就越大。通常「rdb_last_bgsave_time_sec」這個時間取決于兩個因素:

  • REDIS占用的內存大小

  • 磁盤的寫速度。

rdb_last_bgsave_time_sec:85這個標識表示我們上次保存dump RDB文件的時間。這個耗時受限于上面提到的兩個因素。

當redis處于 rdb_bgsave_in_progress狀態時,通過vmstat命令查看性能,得到wa值偏高,也就是說CPU在等待
IO的請求完成,我們線上的一個應用redis占用的內存是5G左右,也就是redis會生成大約5G左右的dump.rdb文件

vmstat命令

r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  4      0 223912 2242680 5722008    0    0   200 48648 3640 5443  1  1 63 35  0
 0  3      0 222796 2242680 5722052    0    0    16 48272 2417 5019  1  1 63 35  0
 0  3      0 222300 2242680 5722092    0    0    40 24612 3042 3568  1  1 63 35  0
 0  3      0 220068 2242680 5722124    0    0    64 40328 4304 4737  2  1 63 34  0
 0  3      0 218952 2242680 5722216    0    0   100 48648 4966 5786  1  2 63 35  0
 0  3      0 215356 2242680 5722256    0    0     0 66168 3546 4382  2  1 62 35  0

通過上面的輸出,看到BGSAVE 對于IO的性能影響比較大

那么該如何解決由RDB文件帶來的性能上不足的問題,又能保證數據持久化的目的

通常的設計思路就是利用「Replication」機制來解決:即master不開啟RDB日志和AOF日志,來保證master的讀寫性能。而slave則開啟rdb和aof來進行持久化,保證數據的持久性,

建立主從復制步驟和災難恢復

我在測試機器上,開啟兩個實例,端口分別為6379和6380

master: 172.16.76.232 6379
slave:  172.16.76.232 6380

修改配置

修改master的redis.conf

關閉RDB

# save 900 1

save 300 10

save 60 10000</pre>

關閉AOF</p>

appendonly no

分別啟動master和slave的redis

service redis start

修改slave配置,指向master服務器

redis-cli > slaveof 172.16.76.232 6379

查看slave的復制狀態

redis-cli > info replication

腳本模擬填充數據

#!/bin/bash

ID=1 while(($ID<50001)) do redis-cli set "my:$ID" "aaa_okthisisOmb5EVIwBgPHgbRj64raygpeRLKaNhyB9sLF$ID" redis-cli set "your:$ID" "your_okthisisOmb5EVIwBgPHgbRj64raygpeRLKaNhyB9sLF$ID" redis-cli set "her:$ID" "her_okthisisOmb5EVIwBgPHgbRj64raygpeRLKaNhyB9sLF$ID" redis-cli set "his:$ID" "his_okthisisOmb5EVIwBgPHgbRj64raygpeRLKaNhyB9sLF$ID"

ID=$(($ID+1)) done</pre>

kill掉master實例模擬災難

master redis > killall -9 redis-server
SLAVE redis > SLAVEOF NO ONE

取消Slave的同步,避免主庫在未完成數據恢復前就重啟,進而直接覆蓋掉從庫上的數據,導致所有的數據丟失。

將slave上的RDB和AOF復制到master數據文件夾中

cp /data/redis_data_slave/dump.rdb /data/redis_data/
cp /data/redis_data_slave/Append.AOF /data/redis_data/

啟動master的實例

master redis > dbsize

查看數據是否恢復

重新開啟slave復制

slave redis > slaveof 172.16.76.232 6379

故障案例報告

redis丟失數據案例

背景介紹:

我們的一臺redis服務器,硬件配置為4核,4G內存。redis持久話方案是RDB。前面幾個月redis使用的

內存在1G左右。在一次重啟之后,redis只恢復了部分數據,這時查看redis.log文件。看見了如下的錯誤

[23635] 25 Jul 08:30:54.059 * 10000 changes in 60 seconds. Saving...
[23635] 25 Jul 08:30:54.059 # Can't save in background: fork: Cannot allocate memory

這時,想起了redis啟動時的警告

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.

翻譯

警告:過量使用內存設置為0!在低內存環境下,后臺保存可能失敗。為了修正這個問題,
請在/etc/sysctl.conf 添加一項 'vm.overcommit_memory = 1' ,
然后重啟(或者運行命令'sysctl vm.overcommit_memory=1' )使其生效。

vm.overcommit_memory不同的值說明

  • 0 表示檢查是否有足夠的內存可用,如果是,允許分配;如果內存不夠,拒絕該請求,并返回一個錯誤給應用程序。

  • 1 允許分配超出物理內存加上交換內存的請求

  • 2 內核總是返回true

redis的數據回寫機制分為兩種

  • 同步回寫即SAVE命令。redis主進程直接寫數據到磁盤。當數據量大時,這個命令將阻塞,響應時間長

  • 異步回寫即BGSAVE命令。redis 主進程fork一個子進程,復制主進程的內存并通過子進程回寫數據到磁盤。

由于RDB文件寫的時候fork一個子進程。相當于復制了一個內存鏡像。當時系統的內存是4G,而redis占用了
近3G的內存,因此肯定會報內存無法分配。如果 「vm.overcommit_memory」設置為0,在可用內存不足的情況
下,就無法分配新的內存。如果 「vm.overcommit_memory」設置為1。 那么redis將使用交換內存。

解決辦法:

  • 方法一: 修改內核參數 vi /etc/sysctl。設置vm.overcommit_memory = 1然后執行

    ``` sysctl -p ```
    </li>
  • 方法二: 使用交換內存并不是一個完美的方案。最好的辦法是擴大物理內存。

  • </ul>

    復制有可能碰到的問題

    使用slaveof命令后,長時間看不到數據同步。以為復制功能失效,或配置錯了。其實,不用擔心,有兩種方法可以確定是否正在建立復制。

    在創建Redis復制時,一開始可能會發現Slave長時間不開始同步數據,可能數據量太大,導致了Master正在dump數據慢,此時如果你在Master上執行「top -p $(pgrep -d, redis-server)」命令,就能看到dump的過程

    方式一: 通過「top」命令

    [root@img1_u ~]# top -p $(pgrep -d, redis-server)
    top - 14:06:24 up 54 days,  6:13,  1 user,  load average: 1.18, 1.32, 1.20
    Tasks:   2 total,   1 running,   1 sleeping,   0 stopped,   0 zombie
    Cpu(s): 15.2%us,  1.7%sy,  0.6%ni, 81.9%id,  0.2%wa,  0.0%hi,  0.4%si,  0.0%st
    Mem:  24542176k total, 22771848k used,  1770328k free,  2245720k buffers
    Swap:   524280k total,        0k used,   524280k free,  4369452k cached

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 21619 root 20 0 5654m 5.4g 388 R 99.9 23.0 0:23.70 redis-server 1663 root 20 0 5654m 5.4g 1068 S 15.3 23.0 5042:31 redis-server</pre>

    redis-server是單進程的,現在通過「top」命令查看已經有2個進程,因為之前提到的,redis在建立復制的時,會在

    主服務器上執行 BGSAVE 命令。fork一個子進程,dump出RDB文件。 master dump 完畢,然后再將快照文件傳給slave。

    方式二:通過「rdb_bgsave_in_progress」標識

    進入master的redis-cli

    redis-cli > info persistence
    ...
    loading:0
    rdb_changes_since_last_save:0
    rdb_bgsave_in_progress:1
    rdb_last_save_time:1448992510
    rdb_last_bgsave_status:ok
    rdb_last_bgsave_time_sec:4
    rdb_current_bgsave_time_sec:1
    ...

    如果「rdb_bgsave_in_progress」為1,那么master正在進行bgsave命令。同時「rdb_current_bgsave_time_sec」
    顯示bgsave命令已經執行的時間。由于在master服務器上默認不開啟RDB和AOF日志,如果「rdb_bgsave_in_progress」為1,那么就可以肯定由于復制原因發送一個「bgsave」指令 dump 出 RDB 文件。

    redis 內存達到上限

    有運營的同事反應,系統在登錄的情況下,操作時會無緣無故跳到登錄頁面。 由于我們的系統做了分布式的
    session,默認把session放到redis里,按照以往的故障經驗。可能是redis使用了最大內存上限
    導致了無法設置key。 登錄 redis 服務器查看 redis.conf 文件設置了最大8G內存「maxmemory 8G」
    然后通過「redis-cli info memory 」 查詢到目前的內存使用情況 「used_memory_human:7.71G」
    接著通過redis-cli工具設置值 。報錯 「OOM command not allowed when used memory 」。再次
    驗證了redis服務器已經達到了最大內存

    解決方法:

    1. 關閉redis 服務器redis-cli shutdown

    2. 修改配置文件的最大內存 「maxmemory」

    3. 啟動redis服務器redis-server redis.conf

    來自:

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!