定期全備redis

        首先申明我的觀點,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

說明:

  1. 所有redis實例使用相同口令。
  2. 遍歷哨兵返回的監控信息,取得每組redis複製的master IP、端口。
  3. 爲避免對master造成影響,連接redis slave實例進行備份,用對3取模獲取slave的IP。
  4. 使用redis-cli --rdb執行備份。
  5. 備份文件名中帶有精確到小時的時間。

        以上腳本在另外的備份機器上每小時定時執行一次:

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!

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