共计 2579 个字符,预计需要花费 7 分钟才能阅读完成。
为了数据安全,数据库须要定期备份,这个大家都懂,然而数据库备份的时候,最怕写操作,因为这个最容易导致数据的不统一,松哥举一个简略的例子大家来看下:
假如在数据库备份期间,有用户下单了,那么可能会呈现如下问题:
- 库存表扣库存。
- 备份库存表。
- 备份订单表数据。
- 订单表增加订单。
- 用户表扣除账户余额。
- 备份用户表。
如果依照下面这样的逻辑执行,备份文件中的订单表就少了一条记录。未来如果应用这个备份文件复原数据的话,就少了一条记录,造成数据不统一。
为了解决这个问题,MySQL 中提供了很多计划,咱们来逐个进行解说并剖析其优劣。
1. 全库只读
要解决这个问题,咱们最容易想到的方法就是在数据库备份期间设置数据库只读,不能写,这样就不必放心数据不统一了,设置全库只读的方法也很简略,首先咱们执行如下 SQL 先看看对应变量的值:
show variables like 'read_only';
能够看到,默认状况下,read_only
是 OFF,即敞开状态,咱们先把它改为 ON,执行如下 SQL:
set global read_only=1;
1 示意 ON,0 示意 OFF,执行后果如下:
这个 read_only
对 super 用户有效,所以设置实现后,接下来咱们退出来这个会话,而后创立一个不蕴含 super 权限的用户,用新用户登录,登录胜利之后,执行一个插入 SQL,后果如下:
能够看到,这个错误信息中说,当初的 MySQL 是只读的(只能查问),不能执行以后 SQL。
加了只读属性,就不必放心备份的时候产生数据不统一的问题了。
然而 read_only
咱们通常用来标识一个 MySQL 实例是主库还是从库:
- read_only=0,示意该实例为主库。数据库管理员 DBA 可能每隔一段时间就会对该实例写入一些业务无关的数据来判断主库是否可写,是否可用,这就是常见的探测主库实例是否活着的。
- read_only=1,示意该实例为从库。每隔一段时间探活,往往只会对从库进行读操作,比方 select 1; 这样进行探活从库。
所以,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_log
,general_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 这样的存储引擎,如果要备份,还是乖乖的应用全局锁吧。