MySQL——数据库日志&主从复制&备份与恢复
写在最后
一些体会写出来:
- 享受工作,享受学习,享受娱乐
- 知识系统
- 阅读大量书
- 一路走来回头看时,一切都很清晰
- 珍惜时间
其它数据库日志
1. MySQL支持的日志
1.1 日志类型
MySQL有不同类型的日志文件,用来存储不同类型的日志,分为二进制日志
、错误日志
、通用查询日志
和慢查询日志
,这也是常用的4种。MySQL 8又新增两种支持的日志:中继日志
和数据定义语句日志
。使用这些日志文件,可以查看MySQL内部发生的事情。
慢查询日志:记录所有执行时间超过long_query_time的所有查询,方便我们对查询进行优化。
通用查询日志:记录所有连接的起始时间和终止时间,以及连接发送给数据库服务器的所有指令,对我们复原操作的实际场景、发现问题,甚至是对数据库操作的审计都有很大的帮助。
错误日志:记录MySQL服务的启动、运行或停止MySQL服务时出现的问题,方便我们了解服务器的状态,从而对服务器进行维护。
二进制日志:记录所有更改数据的语句,可以用于主从服务器之间的数据同步,以及服务器遇到故障时数据的无损失恢复。
中继日志:用于主从服务器架构中,从服务器用来存放主服务器二进制日志内容的一个中间文件。从服务器通过读取中继日志的内容,来同步主服务器上的操作。
数据定义语句日志:记录数据定义语句执行的元数据操作。
除二进制日志外,其他日志都是文本文件
。默认情况下,所有日志创建于MySQL数据目录
中。
1.2 日志的弊端
日志功能会
降低MySQL数据库的性能
。日志会
占用大量的磁盘空间
。
1 | + redo、undo 日志 |
2. 通用查询日志(general query log)
通用查询日志用来记录用户的所有操作
,包括启动和关闭MySQL服务、所有用户的连接开始时间和截止时间、发给 MySQL 数据库服务器的所有 SQL 指令等。当我们的数据发生异常时,查看通用查询日志,还原操作时的具体场景,可以帮助我们准确定位问题。
2.1 查看当前状态
1 | mysql> SHOW VARIABLES LIKE '%general%'; |
2.2 启动日志
方式1:永久性方式
1 | [mysqld] |
方式2:临时性方式
1 | SET GLOBAL general_log=on; # 开启通用查询日志 |
2.3 停止日志
方式1:永久性方式
1 | [mysqld] |
方式2:临时性方式
1 | SET GLOBAL general_log=off; |
3.错误日志(error log)
3.1 启动日志
在MySQL数据库中,错误日志功能是默认开启
的。而且,错误日志无法被禁止
。
1 | [mysqld] |
3.2 查看日志
1 | mysql> SHOW VARIABLES LIKE 'log_err%'; |
3.3 删除\刷新日志
1 | install -omysql -gmysql -m0644 /dev/null /var/log/mysqld.log |
1 | + 记录用户的所有操作 |
4. 二进制日志(bin log)
4.1 查看默认情况
1 | mysql> show variables like '%log_bin%'; |
4.2 日志参数设置
方式1:永久性方式
1 | [mysqld] |
设置带文件夹的bin-log日志存放目录
1 | [mysqld] |
注意:新建的文件夹需要使用mysql用户,使用下面的命令即可。
1 | chown -R -v mysql:mysql binlog |
方式2:临时性方式
1 | # global 级别 |
4.3 查看日志
1 | mysqlbinlog -v "/var/lib/mysql/binlog/atguigu-bin.000002" |
1 | # 可查看参数帮助 |
上面这种办法读取出binlog日志的全文内容比较多,不容易分辨查看到pos点信息,下面介绍一种更为方便的查询命令:
1 | mysql> show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]; |
IN 'log_name'
:指定要查询的binlog文件名(不指定就是第一个binlog文件)FROM pos
:指定从哪个pos起始点开始查起(不指定就是从整个文件首个pos点开始算)LIMIT [offset]
:偏移量(不指定就是0)row_count
:查询总条数(不指定就是所有行)
1 | mysql> show binlog events in 'atguigu-bin.000002'; |
4.4 使用日志恢复数据
mysqlbinlog恢复数据的语法如下:
1 | mysqlbinlog [option] filename|mysql –uuser -ppass; |
filename
:是日志文件名。option
:可选项,比较重要的两对option参数是–start-date、–stop-date 和 –start-position、– stop-position。--start-date 和 --stop-date
:可以指定恢复数据库的起始时间点和结束时间点。--start-position和--stop-position
:可以指定恢复数据的开始位置和结束位置。
注意:使用mysqlbinlog命令进行恢复操作时,必须是编号小的先恢复,例如atguigu-bin.000001必须在atguigu-bin.000002之前恢复。
4.5 删除二进制日志
1. PURGE MASTER LOGS:删除指定日志文件
1 | PURGE {MASTER | BINARY} LOGS TO ‘指定日志文件名’ |
5. 再谈二进制日志(binlog)
5.1 写入机制
binlog的写入时机也非常简单,事务执行过程中,先把日志写到binlog cache
,事务提交的时候,再把binlog cache写到binlog文件中。因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。
write和fsync的时机,可以由参数sync_binlog
控制,默认是 0
。为0的时候,表示每次提交事务都只write,由系统自行判断什么时候执行fsync。虽然性能得到提升,但是机器宕机,page cache里面的binglog 会丢失。如下图:
为了安全起见,可以设置为1
,表示每次提交事务都会执行fsync,就如同redo log 刷盘流程一样。最后还有一种折中方式,可以设置为N(N>1),表示每次提交事务都write,但累积N个事务后才fsync。
在出现IO瓶颈的场景里,将sync_binlog设置成一个比较大的值,可以提升性能。同样的,如果机器宕机,会丢失最近N个事务的binlog日志。
5.2 binlog与redolog对比
- redo log 它是
物理日志
,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎层产生的。 - 而 binlog 是
逻辑日志
,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server 层。 - 虽然它们都属于持久化的保证,但是侧重点不同。
- redo log 让InnoDB存储引擎拥有了崩溃恢复能力。
- binlog保证了MySQL集群架构的数据一致性
5.3 两阶段提交
在执行更新语句过程,会记录redo log与binlog两块日志,以基本的事务为单位,redo log在事务执行过程中可以不断写入,而binlog只有在提交事务时才写入,所以redo log与binlog的写入时机
不一样。
为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。
使用两阶段提交后,写入binlog时发生异常也不会有影响
另一个场景,redo log设置commit阶段发生异常,那会不会回滚事务呢?
并不会回滚事务,它会执行上图框住的逻辑,虽然redo log是处于prepare阶段,但是能通过事务id找到对应的binlog日志,所以MySQL认为是完整的,就会提交事务恢复数据。
1 | + 二进制日志 |
6. 中继日志(relay log)
6.1 介绍
中继日志只在主从服务器架构的从服务器上存在。从服务器为了与主服务器保持一致,要从主服务器读取二进制日志的内容,并且把读取到的信息写入本地的日志文件
中,这个从服务器本地的日志文件就叫中继日志
。然后,从服务器读取中继日志,并根据中继日志的内容对从服务器的数据进行更新,完成主从服务器的数据同步
。
6.2 恢复的典型错误
如果从服务器宕机,有的时候为了系统恢复,要重装操作系统,这样就可能会导致你的服务器名称
与之前不同
。而中继日志里是包含从服务器名
的。在这种情况下,就可能导致你恢复从服务器的时候,无法从宕机前的中继日志里读取数据,以为是日志文件损坏了,其实是名称不对了。
解决的方法也很简单,把从服务器的名称改回之前的名称。
1 | + 中继日志 |
主从复制
1. 主从复制概述
1.1 如何提升数据库并发能力
一般应用对数据库而言都是“读多写少
”,也就说对数据库读取数据的压力比较大,有一个思路就是采用数据库集群的方案,做主从架构
、进行读写分离
,这样同样可以提升数据库的并发处理能力。但并不是所有的应用都需要对数据库进行主从架构的设置,毕竟设置架构本身是有成本的。
如果我们的目的在于提升数据库高并发访问的效率,那么首先考虑的是如何优化SQL和索引
,这种方式简单有效;其次才是采用缓存的策略
,比如使用 Redis将热点数据保存在内存数据库中,提升读取的效率;最后才是对数据库采用主从架构
,进行读写分离。
1.2 主从复制的作用
第1个作用:读写分离。
第2个作用就是数据备份。
第3个作用是具有高可用性。
2. 主从复制的原理
2.1 原理剖析
三个线程
实际上主从同步的原理就是基于 binlog 进行数据同步的。在主从复制过程中,会基于3 个线程
来操作,一个主库线程,两个从库线程。
二进制日志转储线程
(Binlog dump thread)是一个主库线程。当从库线程连接的时候, 主库可以将二进制日志发送给从库,当主库读取事件(Event)的时候,会在 Binlog 上加锁
,读取完成之后,再将锁释放掉。
从库 I/O 线程
会连接到主库,向主库发送请求更新 Binlog。这时从库的 I/O 线程就可以读取到主库的二进制日志转储线程发送的 Binlog 更新部分,并且拷贝到本地的中继日志 (Relay log)。
从库 SQL 线程
会读取从库中的中继日志,并且执行日志中的事件,将从库中的数据与主库保持同步。
复制三步骤
步骤1:Master
将写操作记录到二进制日志(binlog
)。
步骤2:Slave
将Master
的binary log events拷贝到它的中继日志(relay log
);
步骤3:Slave
重做中继日志中的事件,将改变应用到自己的数据库中。 MySQL复制是异步的且串行化的,而且重启后从接入点
开始复制。
复制的问题
复制的最大问题:延时
2.2 复制的基本原则
每个
Slave
只有一个Master
每个
Slave
只能有一个唯一的服务器ID每个
Master
可以有多个Slave
1 | + 数据库-缓存架构 |
3. 同步数据一致性问题
主从同步的要求:
读库和写库的数据一致(最终一致);
写数据必须写到写库;
读数据必须到读库(不一定);
3.1 理解主从延迟问题
进行主从同步的内容是二进制日志,它是一个文件,在进行网络传输
的过程中就一定会存在主从延迟
(比如 500ms),这样就可能造成用户在从库上读取的数据不是最新的数据,也就是主从同步中的数据不一致性
问题。
3.2 主从延迟问题原因
在网络正常的时候,日志从主库传给从库所需的时间是很短的,即T2-T1的值是非常小的。即,网络正常情况下,主备延迟的主要来源是备库接收完binlog和执行完这个事务之间的时间差。
主备延迟最直接的表现是,从库消费中继日志(relay log)的速度,比主库生产binlog的速度要慢。造成原因:
1、从库的机器性能比主库要差
2、从库的压力大
3、大事务的执行
3.3 如何减少主从延迟
若想要减少主从延迟的时间,可以采取下面的办法:
降低多线程大事务并发的概率,优化业务逻辑
优化SQL,避免慢SQL,
减少批量操作
,建议写脚本以update-sleep这样的形式完成。提高从库机器的配置
,减少主库写binlog和从库读binlog的效率差。尽量采用
短的链路
,也就是主库和从库服务器的距离尽量要短,提升端口带宽,减少binlog传输的网络延时。实时性要求的业务读强制走主库,从库只做灾备,备份。
3.4 如何解决一致性问题
读写分离情况下,解决主从同步中数据不一致的问题, 就是解决主从之间 数据复制方式 的问题,如果按照数据一致性 从弱到强 来进行划分,有以下 3 种复制方式。
方法 1:异步复制
方法 2:半同步复制
方法 3:组复制
首先我们将多个节点共同组成一个复制组,在执行读写(RW)事务
的时候,需要通过一致性协议层(Consensus 层)的同意,也就是读写事务想要进行提交,必须要经过组里“大多数人”(对应 Node 节点)的同意,大多数指的是同意的节点数量需要大于 (N/2+1),这样才可以进行提交,而不是原发起方一个说了算。而针对只读(RO)事务
则不需要经过组内同意,直接 COMMIT 即可。
1 | + 主从同步 |
数据库备份与恢复
1. 物理备份与逻辑备份
物理备份:备份数据文件,转储数据库物理文件到某一目录。物理备份恢复速度比较快,但占用空间比较大,MySQL中可以用xtrabackup
工具来进行物理备份。
逻辑备份:对数据库对象利用工具进行导出工作,汇总入备份文件内。逻辑备份恢复速度慢,但占用空间小,更灵活。MySQL 中常用的逻辑备份工具为mysqldump
。逻辑备份就是备份sql语句
,在恢复的时候执行备份的sql语句实现数据库数据的重现。
2. mysqldump实现逻辑备份
2.1 备份一个数据库
1 | mysqldump –u 用户名称 –h 主机名称 –p密码 待备份的数据库名称[tbname, [tbname...]]> 备份文件名 称.sql |
1 | mysqldump -uroot -p atguigu>atguigu.sql #备份文件存储在当前目录下 |
2.2 备份全部数据库
1 | mysqldump -uroot -pxxxxxx --all-databases > all_database.sql |
2.3 备份部分数据库
1 | mysqldump –u user –h host –p --databases [数据库的名称1 [数据库的名称2...]] > 备份文件名 称.sql |
1 | mysqldump -uroot -p --databases atguigu atguigu12 >two_database.sql |
2.4 备份部分表
1 | mysqldump –u user –h host –p 数据库的名称 [表名1 [表名2...]] > 备份文件名称.sql |
1 | mysqldump -uroot -p atguigu book> book.sql |
2.5 备份单表的部分数据
1 | mysqldump -uroot -p atguigu student --where="id < 10 " > student_part_id10_low_bak.sql |
2.6 排除某些表的备份
1 | mysqldump -uroot -p atguigu --ignore-table=atguigu.student > no_stu_bak.sql |
2.7 只备份结构或只备份数据
- 只备份结构
1 | mysqldump -uroot -p atguigu --no-data > atguigu_no_data_bak.sql |
- 只备份数据
1 | mysqldump -uroot -p atguigu --no-create-info > atguigu_no_create_info_bak.sql |
2.8 备份中包含存储过程、函数、事件
1 | mysqldump -uroot -p -R -E --databases atguigu > fun_atguigu_bak.sql |
1 | + 物理备份与逻辑备份 |
3. mysql命令恢复数据
1 | mysql –u root –p [dbname] < backup.sql |
3.1 单库备份中恢复单库
1 | 备份文件中包含了创建数据库的语句 |
3.2 全量备份恢复
1 | mysql –u root –p < all.sql |
3.3 从全量备份中恢复单库
1 | sed -n '/^-- Current Database: `atguigu`/,/^-- Current Database: `/p' all_database.sql > atguigu.sql |
3.4 从单库备份中恢复单表
1 | cat atguigu.sql | sed -e '/./{H;$!d;}' -e 'x;/CREATE TABLE `class`/!d;q' > class_structure.sql |
1 | + 物理备份 |
4. 表的导出与导入
4.1 表的导出
1. 使用SELECT…INTO OUTFILE导出文本文件
1 | SHOW GLOBAL VARIABLES LIKE '%secure%'; |
2. 使用mysqldump命令导出文本文件
1 | mysqldump -uroot -p -T "/var/lib/mysql-files/" atguigu account |
3. 使用mysql命令导出文本文件
1 | mysql -uroot -p --execute="SELECT * FROM account;" atguigu> "/var/lib/mysql-files/account.txt" |
4.2 表的导入
1. 使用LOAD DATA INFILE方式导入文本文件
1 | LOAD DATA INFILE '/var/lib/mysql-files/account_0.txt' INTO TABLE atguigu.account; |
2. 使用mysqlimport方式导入文本文件
1 | mysqlimport -uroot -p atguigu '/var/lib/mysql-files/account.txt' --fields-terminated- by=',' --fields-optionally-enclosed-by='\"' |
5. 数据库迁移
5.1 概述
数据迁移(data migration)是指选择、准备、提取和转换数据,并将数据从一个计算机存储系统永久地传输到另一个计算机存储系统的过程。此外, 验证迁移数据的完整性 和 退役原来旧的数据存储 ,也被认为是整个数据迁移过程的一部分。
数据库迁移的原因是多样的,包括服务器或存储设备更换、维护或升级,应用程序迁移,网站集成,灾难恢复和数据中心迁移。
根据不同的需求可能要采取不同的迁移方案,但总体来讲,MySQL 数据迁移方案大致可以分为 物理迁移 和 逻辑迁移 两类。通常以尽可能 自动化 的方式执行,从而将人力资源从繁琐的任务中解放出来。
5.2 迁移方案
1、相同版本的数据库之间迁移注意点
指的是在主版本号相同的MySQL数据库之间进行数据库移动。
方式1: 因为迁移前后MySQL数据库的 主版本号相同 ,所以可以通过复制数据库目录来实现数据库迁移,但是物理迁移方式只适用于MyISAM引擎的表。对于InnoDB表,不能用直接复制文件的方式备份数据库。
方式2: 最常见和最安全的方式是使用 mysqldump命令 导出数据,然后在目标数据库服务器中使用MySQL命令导入。
2. 不同版本的数据库之间迁移注意点
例如,原来很多服务器使用5.7版本的MySQL数据库,在8.0版本推出来以后,改进了5.7版本的很多缺陷,因此需要把数据库升级到8.0版本
旧版本与新版本的MySQL可能使用不同的默认字符集,例如有的旧版本中使用latin1作为默认字符集,而最新版本的MySQL默认字符集为utf8mb4。如果数据库中有中文数据,那么迁移过程中需要对 默认字符集 进行修改 ,不然可能无法正常显示数据。
高版本的MySQL数据库通常都会 兼容低版本 ,因此可以从低版本的MySQL数据库迁移到高版本的MySQL数据库。
3. 不同数据库之间迁移注意点
不同数据库之间迁移是指从其他类型的数据库迁移到MySQL数据库,或者从MySQL数据库迁移到其他类
型的数据库。这种迁移没有普适的解决方法。迁移之前,需要了解不同数据库的架构, 比较它们之间的差异 。不同数据库中定义相同类型的数据的 关 键字可能会不同 。例如,MySQL中日期字段分为DATE和TIME两种,而ORACLE日期字段只有DATE;SQL Server数据库中有ntext、Image等数据类型,MySQL数据库没有这些数据类型;MySQL支持的ENUM和SET类型,这些SQL Server数据库不支持。
另外,数据库厂商并没有完全按照SQL标准来设计数据库系统,导致不同的数据库系统的 SQL语句 有差别。例如,微软的SQL Server软件使用的是T-SQL语句,T-SQL中包含了非标准的SQL语句,不能和MySQL 的SQL语句兼容。
不同类型数据库之间的差异造成了互相 迁移的困难 ,这些差异其实是商业公司故意造成的技术壁垒。但是不同类型的数据库之间的迁移并 不是完全不可能 。例如,可以使用 MyODBC 实现MySQL和SQL Server之间的迁移。MySQL官方提供的工具 MySQL Migration Toolkit 也可以在不同数据之间进行数据迁移。MySQL迁移到Oracle时,需要使用mysqldump命令导出sql文件,然后, 手动更改 sql文件中的CREATE语句。
迁移小结
1 | + 数据库迁移 |
6. 删库了不敢跑,能干点啥?
1、delete:误删行
恢复数据比较安全的做法,是 恢复出一个备份 ,或者找一个从库作为 临时库 ,在这个临时库上执行这些操作,然后再将确认过的临时库的数据,恢复回主库。如果直接修改主库,可能导致对数据的 二次破坏 。
当然,针对预防误删数据的问题,建议如下:
把 sql_safe_updates 参数设置为 on 。这样一来,如果我们忘记在delete或者update语句中写where条件,或者where条件里面没有包含索引字段的话,这条语句的执行就会报错。
如果确定要把一个小表的数据全部删掉,在设置了sql_safe_updates=on情况下,可以
在delete语句中加上where条件,比如where id>=0。
代码上线前,必须经过 SQL审计 。
2、truncate/drop :误删库/表
这种情况下,要想恢复数据,就需要使用 全量备份 ,加 增量日志 的方式了。这个方案要求线上有定期的全量备份,并且实时备份binlog。在这两个条件都具备的情况下,假如有人中午12点误删了一个库,恢复数据的流程如下:
1、取最近一次 全量备份 ,假设这个库是一天一备,上次备份是当天 凌晨2点 ;
2、用备份恢复出一个 临时库 ;
3、从日志备份里面,取出凌晨2点之后的日志;
4、把这些日志,除了误删除数据的语句外,全部应用到临时库。
3、延迟复制备库
如果有 非常核心 的业务,不允许太长的恢复时间,可以考虑搭建延迟复制的备库。一般的主备复制结构存在的问题是,如果主库上有个表被误删了,这个命令很快也会被发给所有从库,进而导致所有从库的数据表也都一起被误删了。
延迟复制的备库是一种特殊的备库,通过 CHANGE MASTER TO MASTER_DELAY = N 命令,可以指定这个备库持续保持跟主库有 N秒的延迟 。比如你把N设置为3600,这就代表了如果主库上有数据被误删了,并且在1小时内发现了这个误操作命令,这个命令就还没有在这个延迟复制的备库执行。这时候到这个备库上执行stop slave,再通过之前介绍的方法,跳过误操作命令,就可以恢复出需要的数据。
4、预防误删库表的方法
- 账号分离 。这样做的目的是,避免写错命令。比如:
- 只给业务开发同学DML权限,而不给truncate/drop权限。而如果业务开发人员有DDL需求的话,可以通过开发管理系统得到支持。
- 即使是DBA团队成员,日常也都规定只使用 只读账号 ,必要的时候才使用有更新权限的账号。
- 制定操作规范 。比如:
- 在删除数据表之前,必须先 对表做改名 操作。然后,观察一段时间,确保对业务无影响以后再删除这张表。
- 改表名的时候,要求给表名加固定的后缀(比如加 _to_be_deleted ),然后删除表的动作必须通过管理系统执行。并且,管理系统删除表的时候,只能删除固定后缀的表。
5、rm:误删MySQL实例
对于一个有高可用机制的MySQL集群来说,不用担心 rm删除数据 了。只是删掉了其中某一个节点的数据的话,HA系统就会开始工作,选出一个新的主库,从而保证整个集群的正常工作。我们要做的就是在这个节点上把数据恢复回来,再接入整个集群。