定期全备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!

发布了370 篇原创文章 · 获赞 599 · 访问量 218万+

猜你喜欢

转载自blog.csdn.net/wzy0623/article/details/104810655
今日推荐