目录
备份的概念
重要性
- 在生产环境中,数据的安全性至关重要
- 任何数据的丢失都可能产生严重的后果
- 造成数据丢失的原因
- 程序错误
- 人为操作错误
- 运算错误
- 磁盘故障
- 灾难(火灾、地震)和盗窃
备份分类
从物理与逻辑的角度,备份可分为:
- 物理备份:对数据库操作系统中物理文件的备份
- 方法
- 冷备份(脱机备份):是在关闭数据库的时候进行的备份
- 热备份(联机备份):数据库处于运行状态,依赖于数据库的二进制日志文件
- 温备份:数据库锁定表格(不可写入但可读)的状态下进行备份操作
- 方法
- 逻辑备份:对数据库逻辑组件的备份(比如:表;等数据库对象)
从数据库的备份策略的角度,备份可分为:
- 完全备份:每次对数据库进行完整的备份
- 差异备份:只备份自从上次完全备份之后被修改过的文件
- 如果要恢复数据,先恢复最近一次的完全备份,再恢复最近一次的差异备份
- 如果做了多次差异备份,磁盘空间消耗大
- 增量备份:只备份自从上次完全备份或增量备份后被修改的文件
- 如果要恢复数据,先恢复最近一次的完全备份,再逐个恢复这个完全备份下一次的增量备份,直到最近一次的增量备份
- 如果做了多次增量备份,磁盘空间消耗小
常见的备份方法
- 物理冷备
- 备份时数据库处于关闭状态,直接打包数据库文件
- 备份速度快,恢复时也是最简单的
- 专用备份工具mydump或mysqlhotcopy
- mysqldump常用的逻辑备份工具
- mysqlhotcopy仅拥有备份MyISAM和ARCHIVE表
- 启用二进制日志进行增量备份
- 进行增量备份,需要刷新二进制日志
- 第三方工具备份
- 免费的MySQL热备份软件Percona XtraBackup
示例
本博客的实验示例,因为要模拟故障,需要多次恢复快照,所以在安装完MySQL后,创建一个快照以便恢复
冷备份
登入MySQL后,创建一个名为auth的数据库,进入到该数据库后,创建一个用于测试的表
create database auth;
use auth;
create table users (user_name char(20) not null,user_passwd char(50) default '');
添加两行数据,实现以下效果,tom的密码被加密,jerry的密码明文显示
mysql> insert into users values ('tom', password('123'));
mysql> insert into users values ('jerry', '123');
mysql> select * from users;
+-----------+-------------------------------------------+
| user_name | user_passwd |
+-----------+-------------------------------------------+
| tom | *23AE809DDACAF96AF0FD78ED04B6A265E05AA257 |
| jerry | 123 |
+-----------+-------------------------------------------+
使用quit退出
mysql> quit
打包备份
实现冷备份需要mysql处于关闭状态,所以这里关闭mysqld服务
并且创建出存放备份文件的目录
[root@localhost ~]# systemctl stop mysqld
[root@localhost ~]# mkdir /bak
假如我们想要备份整个MySQL的程序和数据文件,那就备份MySQL的整个安装目录
使用tar命令将mysql整个安装目录打包成tar包,存放在/bak/ 目录下,命名为mysql_当天日期.tar.gz
+%F:表示以数字形式显示年月日
mysql/:表示需要打包的目录
[root@localhost ~]# cd /usr/local/
[root@localhost local]# ls
bin etc games include lib lib64 libexec mysql sbin share src
[root@localhost local]# tar zcvf /bak/mysql_$(date +%F).tar.gz mysql/
然后cd到/bak/ 目录下就可以看到生成的tar包
[root@localhost local]# cd /bak/
[root@localhost bak]# ls
mysql_2024-07-26.tar.gz
模拟故障
这里模拟误操作
首先开启数据库服务
[root@localhost bak]# systemctl start mysqld
通过-e选项,将auth数据库删除,然后查询该库下的表,可以看到已经显示ERROR,查询不到了
到这里就完成模拟故障了
[root@localhost bak]# mysql -uroot -ppwd123 -e "drop database auth;"
mysql: [Warning] Using a password on the command line interface can be insecure.
[root@localhost bak]# mysql -uroot -ppwd123 -e "select * from auth.users;"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1146 (42S02) at line 1: Table 'auth.users' doesn't exist
恢复备份
因为备份做的是冷备份,所以恢复备份的时候也需要MySQL是关闭状态
[root@localhost bak]# systemctl stop mysqld
解压备份的tar包,将旧的mysql安装目录重命名备份起来,以防其中的数据可能还会使用
再将解压后的备份目录移动到安装mysql的目录下,实现恢复
[root@localhost bak]# tar zxvf mysql_2024-07-26.tar.gz
[root@localhost bak]# cd /usr/local/
[root@localhost local]# ls
bin etc games include lib lib64 libexec mysql sbin share src
[root@localhost local]# mv mysql/ mysql.bak
[root@localhost local]# mv /bak/mysql /usr/local/mysql
恢复完成后,启动mysqld服务,测试恢复的效果
[root@localhost local]# systemctl start mysqld
临时登录mysql的执行查询语句,可以看到,故障之前的数据被查询出来了,完成恢复
[root@localhost local]# mysql -uroot -ppwd123 -e "select * from auth.users;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------+-------------------------------------------+
| user_name | user_passwd |
+-----------+-------------------------------------------+
| tom | *23AE809DDACAF96AF0FD78ED04B6A265E05AA257 |
| jerry | 123 |
+-----------+-------------------------------------------+
热备份
在MySQL不停机的运行状态下进行备份操作
导出
mysqldump是mysql的一个导出工具,导出的文件实际上是一个脚本文件,运行这个脚本相当于在MySQL中输入了一系列的SQL语句,帮你把原本的对象创建出来
导出表
利用mysqldump来导出指定库中指定表的所有数据到指定的目录下
这里使用指定用户和密码,指定数据库和要在该库中导出的表 > 导出位置
[root@localhost ~]# mysqldump -uroot -ppwd123 auth users > /opt/auth-users.sql
导出库
这里直接去掉表名就是导出指定的数据库到指定位置
[root@localhost opt]# mysqldump -uroot -ppwd123 auth > /opt/auth.sql
导出所有数据库
如果要导出所有的数据库,不用再指定库或表,而是使用--all-databases选项,再指定导出位置就行了
- --opt:优化执行速度
[root@localhost opt]# mysqldump -uroot --opt -ppwd123 --all-databases > /opt/all-data.sql
导入
导入表
导出的表可以导入到任何的数据库里,这里首先创建出一个名为"test"的数据库,用于导入的示例
直接使用mysql命令 指定要导入的数据库,然后使用重定向输入的符号,再指定要导入的文件绝对路径,就可以完成导入
[root@localhost opt]# mysql -uroot -ppwd123 -e "create database test;"
[root@localhost opt]# mysql -uroot -ppwd123 test < /opt/auth-users.sql
导入库
把指定的表换成库名就可以导入数据库了
- 如果要导入的库已经存在,脚本文件会在该库内添加原有的表和其他各种信息
- 如果要导入的库不存在,脚本文件会找不到该库,需要先创建出来该库,再执行
[root@localhost opt]# mysql -uroot -ppwd123 auth < /opt/auth.sql
导入所有数据库
使用mysql命令,不需要指定库或表,直接使用重定向输入,然后指定导入的sql文件即可
[root@localhost opt]# mysql -uroot -ppwd123 < /opt/all-data.sql
增量备份与恢复
增量备份(incremental backup)是备份的一个类型,是指在一次完全备份或上一次增量备份后,以后每次的备份只需备份与前一次相比增加或者被修改的文件。
二进制日志
开启功能
修改mysql配置文件,在[mysqld]模块中添加以下内容,然后重启mysql服务
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
log-bin=mysql-bin
server-id=1
binlog_format=MIXED
[root@localhost ~]# systemctl restart mysqld
- log-bin:日志生成的名称
- server-id:指定服务器的标识,如果要构建一个主从架构的环境,两个设备的标识不能相同
- binlog_format:指定复制方式
- MIXED:混合复制方式
生成日志
此时我们cd进入mysql的data目录下
可以看到生成了两个文件:mysql-bin.000001、mysql-bin.index
如果我们此时使用mysqladmin命令刷新日志文件
[root@localhost ~]# cd /usr/local/mysql/data/
[root@localhost data]# mysqladmin -u root -ppwd123 flush-logs
会发现在data目录下又生成了一个mysql-bin.000002文件
- flush-logs:把当前缓冲区的SQL语句写入到现有的日志文件中(000001),然后生成一个新的日志文件(000002),准备接受新的日志信息
查看日志
使用mysqlbinlog命令查看二进制日志文件,但是此时我们并没有在mysql中执行SQL语句,所以日志文件中也没有记录相关的信息
[root@localhost data]# mysqlbinlog --no-defaults mysql-bin.000001
查看加密内容
在选项中使用base64来解码加密的行
[root@localhost data]# mysqlbinlog --no-defaults --base64-output=DECODE-ROWS mysql-bin.000001
测试
为了验证效果,逐个添加新的数据,并生成一个日志文件
先登录进mysql,然后添加一些数据,然后退出mysql,再刷新日志,此时日志信息就被记录到了000002的文件中
使用mysqlbinlog命令查看二进制日志文件(000002),就可以在日志文件中看到刚刚执行的SQL语句了
[root@localhost data]# mysql -uroot -ppwd123
mysql> use auth
mysql> insert into users values('aaa','123456');
mysql> insert into users values('bbb','123456');
mysql> insert into users values('ccc','123456');
mysql> insert into users values('ddd','123456');
mysql> insert into users values('eee','123456');
mysql> insert into users values('fff','123456');
mysql> quit
[root@localhost data]# mysqladmin -u root -ppwd123 flush-logs
[root@localhost data]# mysqlbinlog --no-defaults --base64-output=DECODE-ROWS mysql-bin.000002
恢复数据
恢复数据的前提是出现了故障,所以我们先模拟出现了故障
模拟故障
将测试恢复的表先删除掉
[root@localhost data]# mysql -u root -ppwd123 -e 'drop table auth.users;'
恢复步骤
先恢复完整备份,然后再通过二进制日志文件逐个恢复增量备份
[root@localhost data]# mysql -u root -ppwd123 auth < /opt/auth.sql
因为该类型日志文件是二进制的不能直接使用,所以先通过mysqlbinlog命令查询出来日志中的内容,通过管道符导入给mysql
然后可以使用select语句查询一下恢复的表,看看表中有没有被写入恢复的数据
[root@localhost data]# mysqlbinlog --no-defaults mysql-bin.000002 | mysql -u root -ppwd123
[root@localhost data]# mysql -u root -ppwd123 -e 'select * from auth.users;'
基于位置恢复
假如在操作数据库时,误删除了一行数据,不要直接insert去添加,而是通过二进制日志文件的位置id来实现精确恢复
模拟故障
再次删除users表,然后先恢复出上一次的完全备份
[root@localhost data]# mysql -u root -ppwd123 -e 'drop table auth.users;'
[root@localhost mysql]# mysql -u root -p auth < /opt/auth.sql
再查看日志内容,从日志中分析哪些数据还没有恢复,找出想要恢复到哪个数据的操作id
日志中带# at 数字 的行表示数据操作的位置id,可以根据开始id和结束id恢来复单独的数据,但是结束id必须包含COMMIT的字段,代表数据存储到硬盘中,否则数据只是被存储在缓冲区内,并没有永久保存
在下方的内容中,开始id就是587,结束id就是732
[root@localhost mysql]# mysqlbinlog --no-defaults mysql-bin.000002
# at 587
#240726 22:44:45 server id 1 end_log_pos 701 CRC32 0xae04e527 Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1722005085/*!*/;
insert into users values('bbb','123456')
/*!*/;
# at 701
#240726 22:44:45 server id 1 end_log_pos 732 CRC32 0x5e9d9312 Xid = 11
COMMIT/*!*/;
# at 732
通过位置id来精确恢复数据
同时指定了开始id和结束id,mysqlbinlog会把两个id中间的操作全部执行一遍
[root@localhost mysql]# mysqlbinlog --no-defaults --start-position='587' --stop-position='732' mysql-bin.000002 | mysql -u root -p
只指定开始id
如果只指定了开始id,代表从开始id的位置,往后的指令全部执行一遍,直到文件末尾
[root@localhost mysql]# mysqlbinlog --no-defaults --start-position='587' mysql-bin.000002 | mysql -u root -p
只指定结束id
如果只指定了结束id,代表从日志开头到结束id的位置,中间的指令全部执行一遍
[root@localhost mysql]# mysqlbinlog --no-defaults --stop-position='521' mysql-bin.000002 | mysql -u root -p
基于时间恢复
在二进制日志文件中,还可以根据记录的时间来恢复数据
比如下方内容中的#240726 22:44:45和#240726 22:45:20 是开始时间和结束时间
把两个时间点改为:2024-07-26 22:44:45和2024-07-26 22:45:20
利用这种格式,就可以使用mysqlbinlog命令进行基于时间的数据恢复
[root@localhost mysql]# mysqlbinlog --no-defaults mysql-bin.000002
# at 587
#240726 22:44:45 server id 1 end_log_pos 701 CRC32 0xae04e527 Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1722005085/*!*/;
insert into users values('bbb','123456')
/*!*/;
# at 701
#240726 22:44:45 server id 1 end_log_pos 732 CRC32 0x5e9d9312 Xid = 11
COMMIT/*!*/;
# at 732
通过时间恢复数据
同时指定了开始时间和结束时间,mysqlbinlog会把两个时间点中间的操作全部执行一遍
[root@localhost mysql]# mysqlbinlog --no-defaults --start-datetime='2024-07-26 22:44:45' --stop-datetime='2024-07-26 22:45:20' mysql-bin.000002 | mysql -u root -p
只指定开始时间
如果只指定了开始时间,代表从开始时间的位置,往后的指令全部执行一遍,直到文件末尾
[root@localhost mysql]# mysqlbinlog --no-defaults --start-datetime='2024-07-26 22:44:45' mysql-bin.000002 | mysql -u root -p
只指定结束时间
如果只指定了结束时间,代表从日志开头到结束时间的位置,中间的指令全部执行一遍
[root@localhost mysql]# mysqlbinlog --no-defaults --stop-datetime='2024-07-26 22:45:20' mysql-bin.000002 | mysql -u root -p