定期全備redis
- 2020 年 3 月 18 日
- 筆記
首先申明我的觀點,redis本身只是快取,不適合作為資料庫使用,有說微博就是拿redis當DB用的,自己去證實吧。如果非要拿redis當資料庫,就不得不考慮數據丟失問題,這裡討論兩種常見的可能造成數據丟失的情況。
第一種情況是redis實例或所在主機宕機,這可以通過複製來解決,再配以redis哨兵機制,實現自動failover。應用通過哨兵訪問redis,當master出現問題,redis自動切換到slave繼續提供服務,整個過程對應用完全透明。這種方式與MySQL router的工作原理非常相似。
第二種情況是用戶錯誤,比如有人誤操作執行了一個flushdb命令。這種情況複製無能為力,因為slave上的數據也同時被刪除了。redis也沒有延遲複製的概念,那麼能想到的就是在持久化上想辦法,比如同時開啟RDB和AOF兩種持久化。還是類比MySQL,RDB相當於dump全備,AOF則像是statement格式的binlog,保存所有redis命令。AOF能保證不丟失數據,當有誤刪除發生,用AOF中保存的命令去重放以恢複數據。但是,AOF本身的實現可能對線上系統產生影響。例如appendfsync或no-appendfsync-on-rewrite參數配置不當,aof-rewrite時就可能發生redis卡頓,我們的生產系統就是因為此原因而放棄了AOF。退一步說,就算AOF可行,真到了恢複數據那一步,重放命令也要執行很長時間。
兩害相權取其輕,既然不開AOF就沒法保證誤操作時的數據丟失,那就用RDB盡量減少損失。參照我們生產redis實際的部署方式,假設有三台物理伺服器,IP為192.168.210.39、192.168.210.40、192.168.210.41。redis採用一主兩從複製,主和從分別部署到不同主機,同時每個主機上通過不同埠開啟多個redis實例。三個實例上再分別啟動一個哨兵實例,同時監控多組redis master。哨兵的監控資訊如下:
# Sentinel sentinel_masters:14 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=redis9,status=ok,address=192.168.210.39:20009,slaves=2,sentinels=3 master1:name=redis5,status=ok,address=192.168.210.39:20005,slaves=2,sentinels=3 master2:name=redis3,status=ok,address=192.168.210.41:20003,slaves=2,sentinels=3 master3:name=redis13,status=ok,address=192.168.210.39:20013,slaves=2,sentinels=3 master4:name=redis7,status=ok,address=192.168.210.40:20007,slaves=2,sentinels=3 master5:name=redis11,status=ok,address=192.168.210.40:20011,slaves=2,sentinels=3 master6:name=redis14,status=ok,address=192.168.210.39:20014,slaves=2,sentinels=3 master7:name=redis6,status=ok,address=192.168.210.39:20006,slaves=2,sentinels=3 master8:name=redis12,status=ok,address=192.168.210.41:20012,slaves=2,sentinels=3 master9:name=redis8,status=ok,address=192.168.210.41:20008,slaves=2,sentinels=3 master10:name=redis2,status=ok,address=192.168.210.40:20002,slaves=2,sentinels=3 master11:name=redis4,status=ok,address=192.168.210.39:20004,slaves=2,sentinels=3 master12:name=redis1,status=ok,address=192.168.210.39:20001,slaves=2,sentinels=3 master13:name=redis10,status=ok,address=192.168.210.41:20010,slaves=2,sentinels=3
可以看到當前總共運行了14組redis主從,埠從20001-20014,每組的主從實例使用相同埠。為分散負載,14個redis master實例分布到不同主機。針對以上部署方式編寫了一個定時自動備份腳本redis_backup.sh,內容如下:
#!/bin/bash # 192.168.210.39、192.168.210.40、192.168.210.41 ~/redis-5.0.3/src/redis-cli -h 192.168.210.39 -p 30001 info | grep address=192.168.210. | while read line do port=`echo $line | awk -F, '{print $3}' | awk -F: '{print $2}'` master_ip=`echo $line | awk -F, '{print $3}' | awk -F: '{print $1}' | awk -F= '{print $2}'` master_ip_last_part=`echo $master_ip | awk -F. '{print $4}'` let slave_ip_last_part=($master_ip_last_part-39+1)%3+39 slave_ip=192.168.210.$slave_ip_last_part password="123456" # echo $master_ip $slave_ip $port $password if [ ! -d "/data/redis/192.168.210.39/$port" ]; then mkdir "/data/redis/192.168.210.39/$port" fi ~/redis-5.0.3/src/redis-cli -p $port -a $password -h $slave_ip --rdb /data/redis/192.168.210.39/$port/dump_`date +%H`.rdb done
說明:
- 所有redis實例使用相同口令。
- 遍歷哨兵返回的監控資訊,取得每組redis複製的master IP、埠。
- 為避免對master造成影響,連接redis slave實例進行備份,用對3取模獲取slave的IP。
- 使用redis-cli –rdb執行備份。
- 備份文件名中帶有精確到小時的時間。
以上腳本在另外的備份機器上每小時定時執行一次:
0 * * * * /data/redis/redis_backup.sh 1>/data/redis/redis_backup.log 2>&1
這個備份方案具有以下特點:
- redis實例自動感知。比如增加了第15套redis主從,只要還是按上述部署方式,備份腳本無需做任何更改,自動生成備份目錄和文件。
#ll /data/redis/192.168.210.39/ total 56 drwxr-xr-x 2 root root 4096 Jan 14 18:22 20001 drwxr-xr-x 2 root root 4096 Jan 14 18:22 20002 drwxr-xr-x 2 root root 4096 Jan 14 18:22 20003 drwxr-xr-x 2 root root 4096 Mar 12 04:11 20004 drwxr-xr-x 2 root root 4096 Jan 14 18:17 20005 drwxr-xr-x 2 root root 4096 Jan 14 18:22 20006 drwxr-xr-x 2 root root 4096 Jan 14 18:18 20007 drwxr-xr-x 2 root root 4096 Jan 14 18:22 20008 drwxr-xr-x 2 root root 4096 Jan 14 18:16 20009 drwxr-xr-x 2 root root 4096 Jan 14 18:23 20010 drwxr-xr-x 2 root root 4096 Jan 14 18:21 20011 drwxr-xr-x 2 root root 4096 Jan 14 18:22 20012 drwxr-xr-x 2 root root 4096 Jan 14 18:18 20013 drwxr-xr-x 2 root root 4096 Jan 14 18:22 20014
- 對於每組redis實例,保留一天的24個備份文件,每小時一個。 #ll /data/redis/192.168.210.39/ total 96 total 3329112 -rw-r–r– 1 root root 142460399 Mar 12 00:18 dump_00.rdb -rw-r–r– 1 root root 142456166 Mar 12 01:18 dump_01.rdb -rw-r–r– 1 root root 141816942 Mar 12 02:18 dump_02.rdb -rw-r–r– 1 root root 141215064 Mar 12 03:20 dump_03.rdb -rw-r–r– 1 root root 140840412 Mar 12 04:17 dump_04.rdb -rw-r–r– 1 root root 140768868 Mar 12 05:17 dump_05.rdb -rw-r–r– 1 root root 141169526 Mar 12 06:17 dump_06.rdb -rw-r–r– 1 root root 141918869 Mar 11 07:17 dump_07.rdb -rw-r–r– 1 root root 142697847 Mar 11 08:18 dump_08.rdb -rw-r–r– 1 root root 142923196 Mar 11 09:18 dump_09.rdb -rw-r–r– 1 root root 142881297 Mar 11 10:19 dump_10.rdb -rw-r–r– 1 root root 142753570 Mar 11 11:23 dump_11.rdb -rw-r–r– 1 root root 142550684 Mar 11 12:24 dump_12.rdb -rw-r–r– 1 root root 142459276 Mar 11 13:25 dump_13.rdb -rw-r–r– 1 root root 142360821 Mar 11 14:20 dump_14.rdb -rw-r–r– 1 root root 142215182 Mar 11 15:22 dump_15.rdb -rw-r–r– 1 root root 142013490 Mar 11 16:20 dump_16.rdb -rw-r–r– 1 root root 141865563 Mar 11 17:22 dump_17.rdb -rw-r–r– 1 root root 141740614 Mar 11 18:21 dump_18.rdb -rw-r–r– 1 root root 141718646 Mar 11 19:23 dump_19.rdb -rw-r–r– 1 root root 141929286 Mar 11 20:24 dump_20.rdb -rw-r–r– 1 root root 142080608 Mar 11 21:23 dump_21.rdb -rw-r–r– 1 root root 142115053 Mar 11 22:23 dump_22.rdb -rw-r–r– 1 root root 141918890 Mar 11 23:18 dump_23.rdb #
- 當有誤操作發生時,最多丟失一小時的數據。
如果絕對不能丟失數據,建議還是用MySQL之類的資料庫吧。再次強調,最好別拿redis當DB!