数据库备份 Golang XORM + mysqldump + rsync

1 数据库备份的分类

1.1 数据备份重要性

在生产环境中,数据的安全性至关重要,任何数据的丢失都可能产生严重的后果。造成数据丢失的原因包括:

  • 程序错误
  • 人为操作失误
  • 运算错误
  • 磁盘故障
  • 灾难(如火灾、地震)和盗窃

1.2 分类

(1)从物理与逻辑的角度,备份可分为:
物理备份:对数据库操作系统的物理文件(如数据文件、日志文件等)的备份。
物理备份方法:

  • 冷备份(脱机备份) :是在关闭数据库的时候进行的,依赖于数据文件。
  • 热备份(联机备份) :数据库处于运行状态,依赖于数据库的日志文件。
  • 温备份:数据库锁定表格(不可写入但可读)的状态下进行备份操作。

逻辑备份:对数据库逻辑组件(如: 表等数据库对象)的备份。

(2)按照数据库的备份策略角度,备份可分为:

  • 完全备份:每次对数据进行完整的备份。
    完全备份是对整个数据库的备份、数据库结构和文件结构的备份;完全备份保存的是备份完成时刻的数据库;完全备份也是增量备份的基础。
    ✔️优点:安全性高;备份与恢复操作简单方便
    ❌缺点:数据存在大量的重复;占用大量的备份空间,空间利用率低;备份与恢复时间长
  • 差异备份:备份那些自从上次完全备份之后被修改过的文件。
  • 增量备份:只有那些在上次完全备份或者增量备份后被修改的文件才会被备份。
    增量备份就是备份自上一次备份之后增加或变化的文件或者内容,主要解决mysqldump命令完全备份存在的备份数据中有重复数据和备份时间与恢复时间长的问题。
    ✔️优点:没有重复数据,效率高,空间利用率最大化;备份量不大,时间短
    ❌缺点:恢复麻烦(需要上次完全备份及完全备份之后所有的增量备份才能恢复,而且要对所有增量备份进行逐个反推恢复);安全性较低
备份方式.png

2 MySQL数据备份

2.1 mysqldump

mysqldump是MySQL自带的备份工具,可方便实现对MySQL的备份,可以将指定的库、表导出为SQL脚本。

(1)mysqldump命令


语法
mysqldump -h 服务器 -u用户名 -p密码 选项与参数 > 备份文件.sql

选项与参数 描述
-A/--all-databases 所有库
-B/--databases db1 db2 多个数据库
db1 数据库名
db1 t1 t2 db1数据库的表t1、t2
-F 备份的同时刷新binlog
-d 仅表结构
-t 仅数据
--lock-all-tables 备份过程中锁住所有表
--lock-tables 如果是备份所有库,那么备份到某个库时只锁某个库,其他库可写

完整语句
mysqldump -uroot -p123123123 --databases db1 db2 > /backup/test.sql

(2)Shell脚本
可以通过shell脚本进行数据库连接、数据库备份及过期备份文件删除。👻

#!/bin/bash

# 配置信息
mysql_user="root" #MySQL备份用户
mysql_password="123123123" #MySQL备份用户的密码
mysql_host="192.168.0.1"
mysql_port="3306"
mysql_charset="utf8" #MySQL编码
backup_db_arr=("db1" "db2" "db3") #要备份的数据库名称,多个用空格分开隔开 如("db1" "db2" "db3")
backup_location=/data/mysql/backup  #备份数据存放位置
expire_backup_delete="ON" #是否开启过期备份删除 ON为开启 OFF为关闭
expire_days=3 #过期时间天数 默认为三天,此项只有在expire_backup_delete开启时有效

backup_time=`date +%Y%m%d%H%M`  #定义备份详细时间
backup_Ymd=`date +%Y-%m-%d` #定义备份目录中的年月日时间
backup_3ago=`date -d '3 days ago' +%Y-%m-%d` #3天之前的日期
backup_dir=$backup_location/$backup_Ymd  #备份文件夹全路径
welcome_msg="Welcome to the MySQL backup tool!" #欢迎语

# 判断MYSQL是否启动,mysql没有启动则备份退出
mysql_ps=`ps -ef |grep mysql |wc -l`
mysql_listen=`netstat -an |grep LISTEN |grep $mysql_port|wc -l`
if [ [$mysql_ps == 0] -o [$mysql_listen == 0] ]; then
        echo "ERROR: failed to start MySQL! Backup stopped!"
        exit
else
        echo $welcome_msg
fi

# 连接到mysql数据库,无法连接则备份退出
mysql -h$mysql_host -P$mysql_port -u$mysql_user -p$mysql_password <<end
use mysql;
select host,user from user where user='root' and host='localhost';
exit
end

flag=`echo $?`
if [ $flag != "0" ]; then
        echo "ERROR: cannot connect MySQL server! Backup stopped!"
        exit
else
        echo "Connected to MySQL! Please wait......"
        # 判断有没有定义备份的数据库,如果定义则开始备份,否则退出备份
        if [ "$backup_db_arr" != "" ];then
                #dbnames=$(cut -d ',' -f1-5 $backup_database)
                #echo "arr is (${backup_db_arr[@]})"
                for dbname in ${backup_db_arr[@]}
                do
                        echo "database $dbname backup starts..."
                        `mkdir -p $backup_dir`
                        `mysqldump -h$mysql_host -P$mysql_port -u$mysql_user -p$mysql_password $dbname --default-character-set=$mysql_charset | gzip > $backup_dir/$dbname-$backup_time.sql.gz`
                        flag=`echo $?`
                        if [ $flag == "0" ];then
                                echo "database $dbname has been backed up to $backup_dir/$dbname-$backup_time.sql.gz succeessfully!"
                        else
                                echo "ERROR: failed to backup database $dbname!"
                        fi
                done
        else
                echo "ERROR: No databases to  be backed up! Backup stopped!"
                exit
        fi
        # 如果开启了删除过期备份,则进行删除操作
        if [ "$expire_backup_delete" == "ON" -a  "$backup_location" != "" ];then
                 #`find $backup_location/ -type d -o -type f -ctime +$expire_days -exec rm -rf {} \;`
                 `find $backup_location/ -type d -mtime +$expire_days | xargs rm -rf`
                 echo "Expired backup data bas been deleted completely!"
        fi
        echo "All database backup has been done successfully!"
        exit
fi

(3)使用XORM备份
可以使用XORM中的以下DumpAllToFile方法对MySQL数据库进行备份。

// DumpAllToFile dump database all table structs and data to a file
func (engine *Engine) DumpAllToFile(fp string, tp ...schemas.DBType) error {
    f, err := os.Create(fp)
    if err != nil {
        return err
    }
    defer f.Close()
    return engine.DumpAll(f, tp...)
}

可以使用beego.toolbox设置定时任务,定时备份数据库。

func RunBackupTask() {
    toolbox.AddTask("backup", toolbox.NewTask("backup", "0 0 2 * * *", ExecBackup))
}

2.2 binlog

binlog是Mysql sever层维护的一种二进制日志,主要是用来记录对mysql数据更新或潜在发生更新的SQL语句,并以"事务"的形式保存在磁盘中。主要作用为:

  • 复制:MySQL Replication在Master端开启binlog,Master把它的二进制日志传递给slaves并回放来达到master-slave数据一致的目的
  • 数据恢复:通过mysqlbinlog工具恢复数据
  • 增量备份

2.3 备份文件压缩

当数据库数据量很大时,.sql文件可能会很大。所以在dump备份完成后,可以对.sql文件进行压缩。

2.4 远程同步到备份服务器

在某些场景下,可能需要将备份文件同步存储到备份服务器上,以避免服务器出现故障时,连同备份文件一起丢失的情况。
Linux中可以用来远程同步、远程备份数据的命令是rsync。rsync 可以理解为 remote sync(远程同步)。
要使用rsync进行远程数据备份,需要在本地和远端都安装rsync。
使用 rsync 命令备份数据时,不会直接覆盖以前的数据(如果数据已经存在),而是先判断已经存在的数据和新数据的差异,只有数据不同时才会把不相同的部分覆盖。


rsync 命令的基本格式有多种,分别是:

[root@localhost ~]# rsync [OPTION] SRC DEST
[root@localhost ~]# rsync [OPTION] SRC [USER@]HOST:DEST
[root@localhost ~]# rsync [OPTION] [USER@]HOST:SRC DEST
[root@localhost ~]# rsync [OPTION] [USER@]HOST::SRC DEST
[root@localhost ~]# rsync [OPTION] SRC [USER@]HOST::DEST

各个参数的含义如下:

  • SRC:用来表示要备份的目标数据所在的位置(路径);
  • DEST:用于表示将数据备份到什么位置;
  • USER@:当做远程同步操作时,需指明系统登录的用户名,如果不显示指定,默认为以 root 身份登录系统并完成同步操作。

针对以上 5 种命令格式,rsync 有 5 种不同的工作模式:

  • 第一种用于仅在本地备份数据;
  • 第二种用于将本地数据备份到远程机器上;
  • 第三种用于将远程机器上的数据备份到本地机器上;
  • 第四种和第三种是相对的,同样第五种和第二种是相对的,它们各自之间的区别在于登陆认证时使用的验证方式不同。

Note:在使用 rsync 在远程传输数据(备份数据)前,是需要进行登陆认证的,这个过程需要借助 ssh 协议或者 rsync 协议才能完成。在 rsync 命令中,如果使用单个冒号(:),则默认使用 ssh 协议;反之,如果使用两个冒号(::),则使用 rsync 协议。
ssh 协议和 rsync 协议的区别在于,rsync 协议在使用时需要额外配置,增加了工作量,但优势是更加安全;反之,ssh 协议使用方便,无需进行配置,但有泄漏服务器密码的风险。

Rsync OPTION选项:

OPTION选项 描述
-a 这是归档模式,表示以递归方式传输文件,并保持所有属性,它等同于-r、-l、-p、-t、-g、-o、-D 选项。-a 选项后面可以跟一个 --no-OPTION,表示关闭 -r、-l、-p、-t、-g、-o、-D 中的某一个,比如-a --no-l 等同于 -r、-p、-t、-g、-o、-D 选项。
-r 表示以递归模式处理子目录,它主要是针对目录来说的,如果单独传一个文件不需要加 -r 选项,但是传输目录时必须加。
-v 表示打印一些信息,比如文件列表、文件数量等。
-l 表示保留软连接。
-p 表示保持文件权限。
-o 表示保持文件属主信息。
-g 表示保持文件属组信息。
-D 表示保持设备文件信息。
-t 表示保持文件时间信息。
--delete 表示删除 DEST 中 SRC 没有的文件。
--exclude=PATTERN 表示指定排除不需要传输的文件,等号后面跟文件名,可以是通配符模式(如 *.txt)。
--progress 表示在同步的过程中可以看到同步的过程状态,比如统计要同步的文件数量、 同步的文件传输速度等。
-u 表示把 DEST 中比 SRC 还新的文件排除掉,不会覆盖。
-z 加上该选项,将会在传输过程中压缩。

在golang中的实现:
(1)调用命令

func Rsync(host, port, user, password, target, src string) error {
    command := fmt.Sprintf("sshpass -p %v rsync -avz --port=%v %v %v@%v::%v", password, port, src, user, host, target)
    cmd := exec.Command("sh", "-c", command)
    output, err := cmd.Output()
    if err != nil {
        fmt.Println("failed to execute Rsync Shell:", command, " with error: ", err.Error())
        return err
    }
    fmt.Println("execute Rsync Shell:", command, " successfully with output:\n", string(output))
    return nil
}

(2)调用shell脚本
shell脚本如下,文件名为rsync.sh,存放在main函数同级的目录中。

#!/bin/sh
sshpass -p$1 rsync  -avz  --port=$2 $3 $4@$5::$6
func Rsync(host, port, user, password, target, src string) error{
    command:=fmt.Sprintf("./rsync.sh %v %v %v %v %v %v",password,port, src, user, host, target)
    cmd := exec.Command("sh", "-c", command)
    output, err := cmd.Output()
    if err != nil {
        fmt.Println("failed to execute Rsync Shell:", command, " with error: ", err.Error())
        return err
    }
    fmt.Println("execute Rsync Shell:", command, " successfully with output:\n", string(output))
    return nil
}

通过以上两种方式都可以进行rsync远程备份。

3 数据库恢复

3.1 利用数据库工具(Navicat)

选择相应的数据库进行运行SQL文件,恢复相应数据库中的结构与数据。

navicat运行sql文件.png

3.2 mysql命令

mysql -u 用户名 -p密码 要恢复的数据库名称 < 库备份脚本的路径/备份文件
例:

mysql -u root -p123123123 db1 < /usr/local/db1.sql

3.3 source命令

执行步骤
step 1 将备份文件拷贝至容器中
docker cp 主机中备份脚本的位置 containerID:容器中要放备份文件的位置
例:docker cp /data/db1.sql b1e067acbbe3:/usr/local/db1.sql
step 2 进入相应的容器中
docker exec -it mysql /bin/bash
step 3 登录mysql
mysql -u用户名 -p密码
step 4 source
use db1;
source /usr/local/db1.sql
如果db1数据库不存在,需要先create database db1;然后use db1;再进行source。

Reference List:
https://blog.csdn.net/qq_46480020/article/details/111603417
https://blog.csdn.net/SHISHIZHIZHI/article/details/121081817
binlog https://zhuanlan.zhihu.com/p/33504555
zip code https://gitee.com/countpoison/youbei
rsync http://c.biancheng.net/view/6121.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355