为了数据安全,需要定期备份数据库。每个人都知道这一点。但是在备份数据库的时候,最怕写操作,因为这样最容易导致数据不一致。宋哥给大家举个简单的例子:假设在数据库备份期间,如果用户下单,可能会出现以下问题:库存扣库存。备份库存表。备份订单表数据。订单表添加订单。用户表扣除账户余额。备份用户表。如果执行上面的逻辑,备份文件中的订单表会少一条记录。如果以后用这个备份文件恢复数据,会少一条记录,造成数据不一致。为了解决这个问题,MySQL提供了很多解决方案,下面我们一一讲解,分析一下它们的优缺点。1.整个数据库只读解决这个问题,我们能想到的最简单的办法就是在数据库备份的时候把数据库设置为只读,不写,这样就不用担心数据了不一致。将整个数据库设置为只读的方法也很简单。首先,我们执行如下SQL,先查看对应变量的值:showvariableslike'read_only';可以看到默认情况下,read_only是OFF,也就是关闭,我们先把它改成ON,执行如下SQL:setglobalread_only=1;1表示ON,0表示OFF,执行结果如下:这个read_only对于超级用户是无效的,所以设置完成后,然后我们退出这个session,然后创建一个没有超级权限的用户,用新用户登录,登录成功后,执行一条insertSQL,结果如下:可以看到报错提示说当前的MySQL是只读的(只能查询),不能执行当前的SQL。增加了只读属性,备份时再也不用担心数据不一致了。但是read_only通常用来标识一个MySQL实例是主库还是从库:read_only=0表示实例是主库。数据库管理员DBA可能每隔一段时间就会向实例写入一些与业务无关的数据,来判断主库是否可写可用。这是检测主库实例是否存活的常用方法。read_only=1,表示该实例为从库。每隔一段时间探查一次,往往只对从库进行读操作,比如select1;这样就探测到了从库。所以read_only属性其实并不适合做备份,而且如果使用read_only属性把整个库设置为readonly,如果客户端出现异常,数据库会一直保持readonly状态,这样会导致整个库长期处于不可写状态,风险很高。因此,该方案不合格。2.全局锁全局锁,顾名思义,就是对整个库加锁。锁定的库不能添加、删除或修改,只能读取。那么让我们看看如何使用全局锁。MySQL提供了一种添加全局读锁的方法,命令是flushtableswithreadlock(FTWRL)。当你需要让整个库都处于只读状态时,可以使用这个命令,然后其他线程的增删改查等操作就会被阻塞。从图中可以看出,flushtableswithreadlock;命令可用于锁定表;解锁表;命令可以完成解锁操作(会话断开时也会自动解锁)。FTWRL与第一节的解决方案相比,有一点改进,就是执行FTWRL命令后如果客户端异常断开,MySQL会自动释放全局锁,整个库恢复到可以被访问的状态正常更新而不总是只读。但!!!加全局锁意味着整个数据库在备份期间处于只读状态,所以只能在数据库备份期间停止业务。所以这种方式不是最好的解决方案。3、Transaction不知道小伙伴们还记不记得之前宋哥给大家分享的数据库隔离级别。四种隔离级别之一是可重复读(REPEATABLEREAD),这也是MySQL默认的隔离级别。在这种隔离级别下,如果用户在另一个事务中多次执行同一条SELECT语句,结果总是一样的。(因为正在执行的事务所产生的数据变化,外部是看不到的)。也就是说,在像InnoDB这样支持事务的存储引擎中,我们可以在备份数据库之前启动一个事务。这时候会先创建一个一致性视图,然后整个事务执行过程中都会使用这个一致性视图,而且因为MVCC的支持,业务在备份期间仍然可以更新数据,这些更新操作不会被当前交易可见。在可重复读的隔离级别下,即使其他事务更新表数据,也不会影响备库的事务读取结果。这就是事务的四大特性的隔离,使得备份时备份的数据始终是事务发生时的开放数据。具体操作也很简单。使用mysqldump备份数据库时,添加--single-transaction参数。为了看到--single-transaction参数的作用,我们可以先启用general_log,general_log是GeneralQueryLog,它记录了MySQL服务器的运行情况。当客户端连接、断开、接收到客户端的SQL语句时,都会将日志写入general_log。开启general_log会损失一定的性能,但是在开发测试环境开启log可以帮助我们加快排错速度。问题。通过下面的查询可以看出general_log默认是没有启用的:我们可以通过修改配置文件my.cnf(Linux)/my.ini(Windows)来增加或修改(如果已有配置项)mysqld下的general_log的值为1,重启mysql服务后修改生效。您还可以通过在MySQL终端上执行setglobalgeneral_log=ON来启用通用日志。此方法不需要重启MySQL。开启后默认日志目录为mysql的数据目录,文件名默认为hostname.log。接下来我们进行不带--single-transaction参数的备份,如下:mysqldump-hlocalhost-uroot-p123test08>test08.sql请注意general_log的默认位置。接下来我们加上--single-transaction参数看看:mysqldump-hlocalhost-uroot-p123--single-transactiontest08>test08.sql大家看我选蓝色的部分,可以看到确实是先启用事务,再启动备份。与不带--single-transaction参数的日志相比,增加了启动事务的部分。4.总结综上所述,添加事务备份似乎是一个不错的选择,但是这个方案也有一个局限性,就是只适用于InnoDB等支持事务的引擎。对于MyISAM这样的存储引擎,如果要备份,还是乖乖的使用全局锁。
