🦑前言:

   日常🦑

🦑方式背景:

为了数据安全,数据库需要定期备份,当数据库备份的时候,这时候最怕数据的 "写" 操作,这样就会造成数据不一致的问题.....

🦑解决方式:

全库只读

全局锁

事务

1:全库只读

 全库只读是在数据库备份的时候,我们去手动的设置数据库只读权限, 参数----read_only

show variables like 'read_only';

由图可见 : read_only 是 OFF,即关闭状态,所以需要设置为打开 "ON";

      1 表示 ON,0 表示 OFF,执行结果如下:

set global read_only=1;

mysql和oss mysql和oss 数据一致性_数据

 

mysql和oss mysql和oss 数据一致性_mysql_02

修改完成以后这里有个问题 read_only 对 super 用户无效:  

所以需要创建一个不包含 super 权限的用户, 使用新的用户登录 然后去执行插入  的操作 

提示报错:  当前mysql是只读权限,不能执行当前sql语句   

所以就不用担心备份的时候发生数据不一致的问题了

mysql和oss mysql和oss 数据一致性_数据_03

但是 read_only 我们通常用来标识一个 MySQL 实例是主库还是从库:

  • read_only=0,表示该实例为主库。数据库管理员 DBA 可能每隔一段时间就会对该实例写入一些业务无关的数据来判断主库是否可写,是否可用,这就是常见的探测主库实例是否活着的。
  • read_only=1,表示该实例为从库。每隔一段时间探活,往往只会对从库进行读操作,比如select 1;这样进行探活从库。

 所以,read_only 这个属性其实并不适合用来做备份,而且如果使用了 read_only 属性将整个库设置为 readonly 之后,如果客户端发生异常,则数据库就会一直保持 readonly 状态,这样会导致整个库长时间处于不可写状态,风险很高。

不合格

2. 全局锁

全局锁:把整个库锁起来,只能读,不能zsgc

  MySQL提供加全局锁的命令:

flush tables with read lock (FTWRL)

这个条命令  让整个库处于只读状态,

mysql和oss mysql和oss 数据一致性_数据_04

 上图  使用 flush tables with read lock; 指令可以锁定表;使用 unlock tables; 指令则可以完成解锁操作(会话断开时也会自动解锁)。

加了全局锁,就意味着整个数据库在备份期间都是只读状态,在数据库备份期间,业务必须暂停

待合格

3. 事务

数据库的隔离级别,四种隔离级别中有一个是可重复读(REPEATABLE READ),这也是 MySQL 默认的隔离级别。

在这个隔离级别下,如果用户在另外一个事务中执行同条 SELECT 语句数次,结果总是相同的。因为正在执行的事务所产生的数据变化不能被外部看到)。

换言之,在 InnoDB 这种支持事务的存储引擎中,那么我们就可以在备份数据库之前先开启事务,此时会先创建一致性视图,然后整个事务执行期间都在用这个一致性视图,而且由于 MVCC 的支持,备份期间业务依然可以对数据进行更新操作,并且这些更新操作不会被当前事务看到

在可重复读的隔离级别下,即使其他事务更新了表数据,也不会影响备份数据库的事务读取结果,这就是事务四大特性中的隔离性,这样备份期间备份的数据一直是在开启事务时的数据。
 

操作步骤:

使用 mysqldump 备份数据库的时候,加上 -–single-transaction 参数即可。

为了看到 -–single-transaction 参数的作用,我们可以先开启 general_log,general_log 即 General Query Log,它记录了 MySQL 服务器的操作。当客户端连接、断开连接、接收到客户端的 SQL 语句时,会向 general_log 中写入日志,开启 general_log 会损失一定的性能,但是在开发、测试环境下开启日志,可以帮忙我们加快排查出现的问题。 

在终端查询发现,默认情况下 general_log 并没有开启:

mysql和oss mysql和oss 数据一致性_数据_05

我们可以通过修改配置文件 my.cnf(Linux)/my.ini(Windows),在 mysqld 下面增加或修改(如已存在配置项)general_log 的值为1,修改后重启 MySQL 服务即可生效。

也可以通过在 MySQL 终端执行 set global general_log = ON 来开启 general log,此方法可以不用重启 MySQL。

 

mysql和oss mysql和oss 数据一致性_mysql_06

开启之后,默认日志的目录是 mysql 的 data 目录,文件名默认为 主机名.log
 

接下来,我们先来执行一个不带 -–single-transaction 参数的备份,如下:

mysqldump -h localhost -uroot -p123 test08 > test08.sql

 

mysql和oss mysql和oss 数据一致性_数据_07

大家注意默认的 general_log 的位置。

接下来我们再来加上 -–single-transaction 参数看看:

        

mysql和oss mysql和oss 数据一致性_mysql_08

看蓝色部分选中 ,可以看到,确实先开启了事务,然后才开始备份的,对比不加 -–single-transaction 参数的日志,多了开启事务这一部分。

小结:

     加事务备份似乎是一个不错的选择,不过这个方案也有一个局限性,那就是只适用于支持事务的引擎如 InnoDB,对于 MyISAM 这样的存储引擎,如果要备份,还是乖乖的使用全局锁吧。

mysqldump对不同类型的存储引擎,内部实现也不一样。主要是针对两种类型的存储引擎:支持事务的存储引擎(如InnoDB)和不支持事务的存储引擎(如MyISAM),下面分别看看这两种存储引擎的实现:

1、对于支持事务的引擎如InnoDB,参数上是在备份的时候加上--single-transaction保证数据一致性
--single-transaction实际上通过做了下面两个操作:

  1. ①、在开始的时候把该session的事务隔离级别设置成repeatable read;
  2. ②、然后启动一个事务(执行bigin),备份结束的时候结束该事务(执行commit)

有了这两个操作,在备份过程中,该session读到的数据都是启动备份时的数据(同一个点)。可以理解为对于innodb引擎来说加了该参数,备份开始时就已经把要备份的数据定下来了,备份过程中的提交的事务时是看不到的,也不会备份进去。


# mysqldump -u root -p --single-transaction --master-data --flush-log --database test > test.sql                             --> --flush-log 表示备份开始之后的更行都切到下一个二进制日志

可以在备份的test.sql文件中前几行看到记录着备份当时的二进制日志信息

# vim test.sql
--CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000004', MASTER_LOG_POS=436263492;
---- Current Database: `test`
.....
# mysqlbinlog --start-position=436263492 mysql-bin.000004 > 00004.sql                     -->在全备恢复之后,我们可以通过之后的二进制日志进行恢复

另外解释下mysqldump备份时为什么要锁表才能保持数据的一致性:

mysql和oss mysql和oss 数据一致性_数据_09

上图说明:

说明:

1、在t1时间点,用mysqldump启动不锁表备份;

2、先导出a表,共耗时5分钟,因为没有锁表,在这5分钟内b表insert了10行数据;

3、到了t2时间点,a表导出完成,开始导出b表;

4、导出b表耗时10分钟,在导出b表的过程中,a、b表均没有数据变化;

5、到了t3时间点,b表导出完成,全部备份结束;

6、然后备机从t1时间点的binlog位置开始应用binlog,最后备机中b表的数据比主机多10行,数据不一致。


2、对于不支持事务的引擎如MyISAM,只能通过锁表来保证数据一致性,这里分三种情况:

  1. ①、导出全库:加--lock-all-tables参数,这会在备份开始的时候启动一个全局读锁(执行flush tables with read lock),其他session可以读取但不能更新数据,备份过程中数据没有变化,所以最终得到的数据肯定是完全一致的;
  2. ②、导出单个库:加--lock-tables参数,这会在备份开始的时候锁该库的所有表,其他session可以读但不能更新该库的所有表,该库的数据一致;
  3. ③、导出单个表:加--lock-tables参数,这会在备份开始的时候锁该表,其他表不受影响,该表数据一致

上面只是展示了对不同引擎来讲加的参数只是为了让数据保持一致性,但在备份中业务并没有停止,时刻可能有新的数据进行写入,为了让我们知道备份时是备份了哪些数据,或者截止到那个指针(二进制日志),我们可以再加入 --master-data参数,备份好的sql文件就会记录从备份截至到哪个指针,指针之后的数据更新我们可以通过二进制日志进行恢复。