MySQL如何保证备份数据的一致性详解
作者:_江南一点雨 发布时间:2024-01-19 17:25:45
前言
为了数据安全,数据库需要定期备份,这个大家都懂,然而数据库备份的时候,最怕写操作,因为这个最容易导致数据的不一致,松哥举一个简单的例子大家来看下:
假设在数据库备份期间,有用户下单了,那么可能会出现如下问题:
库存表扣库存。
备份库存表。
备份订单表数据。
订单表添加订单。
用户表扣除账户余额。
备份用户表。
如果按照上面这样的逻辑执行,备份文件中的订单表就少了一条记录。将来如果使用这个备份文件恢复数据的话,就少了一条记录,造成数据不一致。
为了解决这个问题,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 这样的存储引擎,如果要备份,还是乖乖的使用全局锁吧。
来源:https://blog.csdn.net/u012702547/article/details/124038595


猜你喜欢
- 一、爬虫的简单理解1. 什么是爬虫?网络爬虫也叫网络蜘蛛,如果把互联网比喻成一个蜘蛛网,那么蜘蛛就是在网上爬来爬去的蜘蛛,爬虫程序通过请求u
- 要做好一个HTML邮件,说简单,还真不怎么复杂,说它复杂,其实也不难。-_-!!以前写过类似的[ 关于HTML邮件的总结 ],最近又犯愁,就
- <html> <head> <title>51windows.Net </title> &l
- Python用Pillow(PIL)进行简单的图像操作方法颜色与RGBA值计算机通常将图像表示为RGB值,或者再加上alpha值(通透度,透
- 其实golang用一个函数可以构建一个并发队列,现在编写一个灵活可控的队列程序先定义一个工作type Worker struct { &nb
- 利用Python写了一个小脚本想要传给使用Windows但没有装Python的朋友执行,这时候就可以利用将档案包装成exe档案,让没有Pyt
- 一 .概述SQL Server 将某些系统事件和用户定义事件记录到 SQL Server 错误日志和 Microsoft Windows 应
- 本文实例为大家分享了python实现图片中文字分割的具体代码,供大家参考,具体内容如下1、原始图片(包含数字):结果图:2、原始图片(包含文
- 想要使用xpath来解析html内容, PHP自带两个对象DOMDocument,DOMXpath,其中初始化 loadHtml一般都会报很
- 本文实例为大家分享了微信小程序实现登陆注册滑块验证的具体代码,供大家参考,具体内容如下一、创建自定义组件MoveVerifyMoveVeri
- 如果说goroutine是Go语言程序的并发体的话,那么channels则是它们之间的通信机制。一个channel是一个通信机制,它可以让一
- 应用场景在数据表中,要记录的每条数据是什么时候创建的,不需要应用程序去特意记录,而是由数据库获取当前时间自动记录创建时间。在数据库中,要记录
- js中报404是经常出现的问题,下列是一些高频原因;<script src="${pageContext.request.c
- 在索引列上使用函数使得索引失效的是常见的索引失效原因之一,因此尽可能的避免在索引列上使用函数。尽管可以使用基于函数的索引来解决索引失效的问题
- 创建 NumPy ndarray 对象NumPy 用于处理数组,NumPy 中的数组对象称为 ndarray。我们可以使用 array()
- 在这篇文章中,我将努力揭开Mobile Web开发的神秘面纱,换句话说,也就是为了移动设备上的用户体验可以被接受,代码得怎么设计。我将阐述“
- 传统的HTML页面中连动下拉框采用了两种方法:1)直接将下拉框中的内容hardcode于html的javascript中,调用javascr
- 反射什么是反射? - 反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省) 面向对象中的反射&nb
- 本文实例为大家分享了python实现TCP文件接收发送的具体代码,供大家参考,具体内容如下下一篇分享:udp收发的实现先运行服务器端打开接收
- 当我们采用s=[[0]*3]*2初始化一个数组,然后对s[0][0]进行赋值,改变的是第一列所有的值。因为用s = [[0]*3]*2 初始