How does MySQL ensure the consistency of backup data?

For data security, the database needs to be backed up regularly. Everyone knows this. However, when the database is backed up, the most afraid of write operations, because this is the most likely to cause data inconsistency, Song Ge gives a simple example for everyone to look at:

Assuming that during the database backup, a user places an order, the following problems may occur:

  • Inventory Buckle Inventory.
  • Back up inventory tables.
  • Backup order table data.
  • Add an order to the order form.
  • The user table deducts the account balance.
  • Back up the user table.

If the above logic is followed, there will be one less record in the order table in the backup file. If you use this backup file to restore data in the future, there will be one less record, resulting in data inconsistency.

In order to solve this problem, MySQL provides many solutions, let's explain them one by one and analyze their advantages and disadvantages.

1. The entire library is read-only

To solve this problem, the easiest way we can think of is to set the database to be read-only during database backup and not be able to write, so that you don’t have to worry about data inconsistency. The way to set the entire database to be read-only is also very simple. First, let’s execute the following SQL first. Look at the value of the corresponding variable:

show variables like 'read_only';
复制代码

It can be seen that by default, it read_onlyis OFF, that is, the closed state. We first change it to ON and execute the following SQL:

set global read_only=1;
复制代码

1 means ON, 0 means OFF, the execution result is as follows:

This is invalid read_onlyfor the super user, so after the setting is completed, we will log out of the session, then create a user that does not contain super privileges, log in with the new user, and execute an insert SQL after the login is successful. The result is as follows:

As you can see, the error message says that the current MySQL is read-only (query only) and cannot execute the current SQL.

With the read-only attribute added, there is no need to worry about data inconsistency during backup.

But read_onlywe usually use it to identify whether a MySQL instance is a master or a slave:

  • read_only=0, indicating that the instance is the main library. The database administrator DBA may write some business-unrelated data to the instance every once in a while to determine whether the main database is writable and available. This is a common detection of whether the main database instance is alive.
  • read_only=1, indicating that the instance is a slave library. Every once in a while, the slave library is often only read, such as select 1; this way, the slave library is probed.

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

因此这种方案不合格。

2. 全局锁

全局锁,顾名思义,就是把整个库锁起来,锁起来的库就不能增删改了,只能读了。

那么我们看看怎么使用全局锁。MySQL 提供了一个加全局读锁的方法,命令是 flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的增删改等操作就会被阻塞。

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

和第一小节的方案相比,FTWRL 有一点进步,即:执行 FTWRL 命令之后如果客户端发生异常断开,那么 MySQL 会自动释放这个全局锁,整个库回到可以正常更新的状态,而不会一直处于只读状态。

但是!!!

加了全局锁,就意味着整个数据库在备份期间都是只读状态,那么在数据库备份期间,业务就只能停摆了。

所以这种方式也不是最佳方案。

3. 事务

不知道小伙伴们是否还记得松哥之前和大家分享的数据库的隔离级别,四种隔离级别中有一个是可重复读(REPEATABLE READ),这也是 MySQL 默认的隔离级别。

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

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

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

具体操作也很简单,使用 mysqldump 备份数据库的时候,加上 -–single-transaction 参数即可。

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

通过如下查询我们可以看到,默认情况下 general_log 并没有开启:

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

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

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

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

mysqldump -h localhost -uroot -p123 test08 > test08.sql
复制代码

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

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

mysqldump -h localhost -uroot -p123 --single-transaction test08 > test08.sql
复制代码

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

4. 小结

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

Guess you like

Origin juejin.im/post/7084089618341036062
Recommended