技术分析 | 通过DML语句浅谈binlog和redo log

欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答

  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。

1. 导言

我们在采用InnoDB引擎的情况下,编写SQL语句WHERE条件的时候,可以通过AND操作符来提高数据的过滤能力,当然,在使用AND操作符也有许多需要注意的地方,就好比一条更新语句:

mysql>update testtable set price = 52.0,name = 'test_name';

本意是将表内对应字段重置为指定信息,可是现在不小心写成了:

mysql>update testtable set price = 52.0 and name = 'test_name';

这个时候,我们查看表发现:

mysql> select * from testtable;
+----+----------+------------+
| id | name     | price      |
+----+----------+------------+
| 1  | old_name |  0.00      |
| 2  | old_name |  0.00      |
+----+----------+------------+

显然,系统会把AND当做判断条件进行逻辑判断,这里所有列都不满足条件,所以结果为0。此时,我们可以利用事务回滚特性,回滚这个update,而为了更好地理解,我们需要重新来看看这一条update语句

2. binlog和redo log

现有一条SQL语句:

mysql>update testtable set price = price + 1 where id = 5;

我们知道,这条语句在分析器是进行词法和语法解析,并且知道这是一条更新语句。并由优化器决定要使用id这个索引。然后,执行器负责具体执行,找到这一行,然后更新。说到更新流程,说到更新操作,我们不得不提到两个日志模块:binlog和redo log

2.1 binlog

先来说一说binlog,该日志存在于Server层次中,是使用存储引擎都可以使用的日志模块,binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给id=5这一行的price字段值加1” binlog的日志文件是可以追加写入的。“追加写入”是指binlog日志文件写到一定大小后会切换到下一个文件进行写入,可以设置sync_binlog为1,让每次事务的binlog都持久化保存到磁盘中

2.2 redo log

而重做日志redo log是MySQL中InnoDB引擎才有的日志,它是物理日志,即记录的内容是“在某个数据页上做了什么修改“

MySQL对于每一次的更新操作如果都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,过程中的IO成本、查找成本都很高。redo log有效的提升了更新效率

redo log是固定大小的,比如可以配置为一组4个文件,每个文件的大小是1GB,那么总共就可以有4GB的空间去记录我们进行的操作。从头开始写,写到末尾就又回到开头循环写,如下图:

file

write pos是当前记录的位置,一边写一边后移,写到第log_3文件末尾后就回到log_0文件开头。checkpoint是当前要释放的位置,也是往后推移并且循环的,释放记录前要把记录更新到数据文件。如果write pos追上checkpoint,那就表明全部空间都满了,这时候不能再执行新的更新,得停下来先释放掉一些位置,让checkpoint继续推进

当有一条记录需要更新的时候,InnoDB引擎就会先将数据更新到内存,再把记录写到redo log里面,这个时候更新就算完成了。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做,可以设置 innodb_flush_log_at_trx_commit为1,表示每次事务的redo log都直接持久化到磁盘中

2.3 binlog和redo log的区别

file

3. InnoDB引擎下模拟执行过程

可参考下图:

file

  • 执行器先通过引擎取到id=5的这一行。id是为该表主键,因此引擎根据BTREE索引查找找到id=5的这一行。如果该行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回

  • 执行器拿到引擎给的行数据,把price值加上1,得到新的一行数据,再调用引擎接口写入这行新数据

  • 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。执行器生成这个操作的binlog,并把binlog写入磁盘

  • 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交commit状态,更新完成

Enjoy GreatSQL :)

文章推荐:

GreatSQL MGR FAQ https://mp.weixin.qq.com/s/J6wkUpGXw3YkyEUJXiZ9xA

万答#12,MGR整个集群挂掉后,如何才能自动选主,不用手动干预 https://mp.weixin.qq.com/s/07o1poO44zwQIvaJNKEoPA

『2021数据技术嘉年华·ON LINE』:《MySQL高可用架构演进及实践》 https://mp.weixin.qq.com/s/u7k99y6i7riq7ScYs7ySnA

一条sql语句慢在哪之抓包分析 https://mp.weixin.qq.com/s/AYibbzl860D90rOeyjB6IQ

万答#15,都有哪些情况可能导致MGR服务无法启动 https://mp.weixin.qq.com/s/inSGpd0Q_XIl2Mb-VsvNsA

技术分享 | 为什么MGR一致性模式不推荐AFTER https://mp.weixin.qq.com/s/rNeq479RNsklY1BlfKOsYg

关于 GreatSQL

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。

Gitee: https://gitee.com/GreatSQL/GreatSQL

GitHub: https://github.com/GreatSQL/GreatSQL

Bilibili: https://space.bilibili.com/1363850082/video

微信&QQ群: 可搜索添加GreatSQL社区助手微信好友,发送验证信息“加群”加入GreatSQL/MGR交流微信群

QQ群:533341697 微信小助手:wanlidbc

本文由博客一文多发平台 OpenWrite 发布!

<think>我们正在解决一个非常具体的技术问题:如何在不停止MySQL服务的情况下,确保内存中的数据redo日志持久化到binlog,以便在虚拟机快照时保证数据一致性。 用户需求的核心点: 1. 在线操作:不能停止MySQL服务。 2. 持久化目标:需要将内存中的数据redo日志持久化到binlog(注意:这里用户可能有一个误解,因为binlogredo log是两种不同的日志,它们之间没有直接的持久化关系。但用户的实际需求是:在虚拟机快照时,保证数据一致性,即要求binlogredo log都处于一个一致的状态,并且能够用于恢复)。 3. 虚拟机快照:当进行虚拟机级别的快照时,需要确保MySQL实例在快照点处于一致状态,这样在恢复时才能保证数据不丢失且一致。 分析: 在MySQL中,为了保证数据的一致性,特别是在进行备份或快照时,我们需要确保两点: a. 数据文件(例如ibdata1, *.ibd)日志文件(redo log, binlog)在某个时间点是一致的。 b. 在快照完成后,binlog必须包含所有在快照点之前已提交的事务,这样在恢复时才能通过binlog进行进一步的数据恢复(如果需要)。 然而,用户提到“将内存或redo日志持久化到binlog”,这实际上涉及MySQL的日志机制。我们需要澄清: - redo log是InnoDB存储引擎层的物理日志,记录的是对数据页的修改,用于崩溃恢复。 - binlogMySQL服务器层的逻辑日志,记录的是语句的原始逻辑(如SQL语句),用于数据复制基于时间点的恢复。 在MySQL中,事务的提交过程采用“两阶段提交”(2PC)来确保redo logbinlog的一致性。具体步骤为: 1. 写入redo log(prepare状态) 2. 写入binlog 3. 写入redo log(commit状态) 因此,要保证在快照时redo logbinlog一致,我们需要确保在快照点没有处于两阶段提交中间状态的事务。同时,为了在恢复时能够使用binlog,我们需要确保binlog已经持久化到磁盘。 那么,如何在不停止服务的情况下,为虚拟机快照准备一个一致的状态? 解决方案: 我们可以使用MySQL的全局锁(FTWRL)或者InnoDB的事务一致性备份(通过START TRANSACTION WITH CONSISTENT SNAPSHOT)来获取一致性状态。但是,用户要求的是在线操作,并且特别提到了binlog。 实际上,对于虚拟机级别的快照,我们通常需要配合文件系统快照(如LVM快照)来实现。在快照之前,我们需要让MySQL处于一个一致的状态,然后立即创建快照。具体步骤: 1. 锁定数据库:执行`FLUSH TABLES WITH READ LOCK`(简称FTWRL)命令。这会关闭所有打开的表,并给所有表加上全局读锁。此时,写操作会被阻塞,但读操作可以继续(因为读锁是共享的)。 2. 获取当前的binlog位置:在锁住之后,立即记录当前的binlog文件名位置(使用`SHOW MASTER STATUS`)。 3. 同时,我们还需要确保redo log已经被刷新到磁盘(实际上,在FTWRL期间,InnoDB会暂停写操作,并且redo log也会被刷新)。 4. 然后,执行文件系统快照(例如LVM快照)。 5. 快照完成后,解锁:`UNLOCK TABLES`。 但是,FTWRL会阻塞写操作,对业务有一定影响(虽然时间很短,仅取决于快照创建的时间)。 另一种更推荐的方法是使用InnoDB的事务一致性备份,即使用`mysqldump`加上`--single-transaction`参数,但这主要用于逻辑备份,而不是虚拟机快照。 对于物理备份,Percona的XtraBackup工具可以在不锁定数据库的情况下进行热备份,它通过记录LSN(日志序列号)并持续拷贝redo日志来保证一致性。但是,用户要求的是虚拟机快照,而不是物理备份。 那么,有没有一种方法可以在不阻塞写操作的情况下,让MySQL在快照点处于一致状态?实际上,MySQL 5.6之后提供了`FLUSH TABLES FOR BACKUP`命令(由Percona Server首先引入,后来在MySQL 5.6中通过备份锁实现)。在MySQL 5.6及以上版本,我们可以: 1. 执行`LOCK TABLES FOR BACKUP`(这是Percona ServerMySQL Enterprise Backup中的命令,在社区版中,我们可以使用`FLUSH TABLES WITH READ LOCK`的替代方法,但社区版没有直接的非阻塞备份锁)。 2. 记录binlog位置。 3. 执行快照。 4. 解锁。 但是,在MySQL社区版中,我们通常使用FTWRL,因为它是最通用的方法。 然而,用户特别提到“确保内存或redo日志持久化到binlog”。实际上,我们无法直接控制redo logbinlog的持久化时机,但我们可以通过命令强制刷新日志: - `FLUSH LOGS`:会关闭当前的二进制日志文件并打开一个新的,同时也会刷新redo log(InnoDB会刷新redo log buffer到磁盘)?注意:`FLUSH LOGS`会刷新所有日志,包括binlogrelay log(如果是从库),但不会刷新InnoDB的redo log buffer。刷新redo log buffer的命令是`FLUSH ENGINE LOGS`,但通常我们使用`FLUSH BINARY LOGS`来刷新binlog,而InnoDB的redo log buffer的刷新是由事务提交每秒刷新线程控制的。 在快照之前,我们通常需要确保所有日志都刷新到磁盘,包括redo logbinlog。我们可以通过以下命令: - `FLUSH TABLES`:关闭所有打开的表,并刷新表到磁盘(但不会加锁,所以其他线程可以立即重新打开表并写入)。 - `FLUSH BINARY LOGS`:关闭当前的binlog文件并创建一个新的,这样就会将当前的binlog文件持久化(因为关闭时会强制写入)。 - 对于redo log,我们可以通过提交一个空事务来强制刷新redo log buffer(因为每个事务提交都会将redo log buffer刷新到磁盘)。但是,这并不能保证所有未提交的事务的redo log也被刷新(因为未提交的事务的redo log可能还在buffer中,但InnoDB在事务提交前不会强制刷新这些redo log)。不过,在FTWRL期间,InnoDB会暂停写操作,并且会将redo log buffer刷新到磁盘。 因此,结合用户的需求,我们给出以下步骤: 步骤1:执行`FLUSH TABLES WITH READ LOCK`(FTWRL)。这个命令会: - 关闭所有打开的表 - 释放所有表锁(包括显式锁定的表) - 获取全局读锁,阻止任何写操作(包括DDLDML中的写) - 同时,在FTWRL执行期间,InnoDB会将其redo log buffer刷新到磁盘(因为InnoDB在准备进入锁定状态时会进行刷新)[^3][^4]。 步骤2:使用`SHOW MASTER STATUS`记录当前的binlog文件名位置。 步骤3:执行虚拟机快照(例如,使用虚拟化平台提供的快照功能)。 步骤4:执行`UNLOCK TABLES`释放锁。 注意:整个过程中,步骤1步骤4之间的时间应该尽可能短,因为在这期间数据库会阻塞所有写操作(包括DDLDML中的写)。如果数据库很大,快照时间较长,可能会影响业务。 另外,对于使用InnoDB存储引擎,我们还可以考虑另一种方法:使用事务一致性快照。但是,这需要备份工具支持(如XtraBackup),而不是虚拟机快照。 如果用户使用的是Percona Server或者MySQL 5.6+,并且支持备份锁,则可以减少锁的粒度: 1. 执行`LOCK TABLES FOR BACKUP`(这只会阻塞非InnoDB表的写操作,以及所有表的DDL操作,但不会阻塞InnoDB表的DML操作)[^3]。 2. 记录binlog位置。 3. 执行快照。 4. 解锁:`UNLOCK TABLES`。 但是,在标准的MySQL社区版中,没有`LOCK TABLES FOR BACKUP`命令,所以我们只能使用FTWRL。 此外,还有一种方法:在快照之前,先设置MySQL为只读模式(`SET GLOBAL read_only=ON;`),然后执行`FLUSH TABLES``FLUSH BINARY LOGS`,记录binlog位置,再执行快照,最后设置回读写模式。这种方法同样会阻塞写操作,但可以防止在快照过程中有新的写入。 总结命令步骤(标准MySQL社区版): 1. 连接到MySQL,执行:`FLUSH TABLES WITH READ LOCK;` 2. 执行:`SHOW MASTER STATUS;` 记录下FilePosition。 3. 在操作系统中执行虚拟机快照命令(例如,在VMware中创建快照)。 4. 回到MySQL,执行:`UNLOCK TABLES;` 注意:在执行FTWRL之前,最好先设置一个会话级别的等待超时,以防止在获取锁时被阻塞太久。例如: `SET SESSION innodb_lock_wait_timeout=50;` 另外,为了确保在快照时binlogredo log都已经被刷新,在FTWRL之后,我们还可以执行一次`FLUSH BINARY LOGS`(但注意,这会导致binlog切换到新文件,所以记录的位置应该是切换前的最后一个文件)。实际上,在FTWRL期间,MySQL会刷新所有表,包括关闭所有打开的表,这也会将相关的日志刷新到磁盘。 因此,用户要求的“在线持久化内存redo日志到binlog”这个说法并不准确,我们实际上是通过锁机制来确保在快照点所有数据文件、redo logbinlog处于一致状态,并且binlog已经包含了所有在快照点之前提交的事务。 最后,我们强调:在快照过程中,数据库的写操作会被暂时阻塞,因此需要在业务低峰期进行,并且尽量缩短快照时间。 如果用户不能接受任何写阻塞,则可以考虑使用Percona XtraBackup进行物理备份,然后对备份文件进行快照,但这已经不是虚拟机级别的快照了。 相关问题: 1. 在MySQL中,FTWRL(FLUSH TABLES WITH READ LOCK)会阻塞哪些操作? 2. 除了FTWRL,还有哪些方法可以在线创建MySQL的一致性快照? 3. 如何减少MySQL在备份期间的锁等待时间? 4. 使用Percona XtraBackup实现热备份的步骤是什么?</think>### MySQL 在线持久化内存 redo 日志到 binlog 的方法 在虚拟机快照时保证数据一致性,关键在于确保所有已提交事务的修改都持久化到 binlog。以下是具体操作步骤技术原理: #### 1. **强制持久化机制** ```sql -- 刷新所有日志到磁盘(包括 redo log binlog) FLUSH BINARY LOGS; -- 强制创建新binlog文件并持久化当前binlog[^4] SET GLOBAL innodb_fast_shutdown = 0; -- 关闭InnoDB快速关闭模式[^3] ``` #### 2. **事务一致性快照流程 ```bash # 步骤1: 锁定所有表(非阻塞式) mysql -e "FLUSH TABLES WITH READ LOCK;" # 步骤2: 获取当前binlog位置 mysql -e "SHOW MASTER STATUS" > binlog_pos.txt # 步骤3: 创建虚拟机快照(此时内存数据已持久化) vmware-toolbox-cmd snapshot create # 步骤4: 释放锁 mysql -e "UNLOCK TABLES;" ``` #### 3. **技术原理说明** 1. **日志写入顺序** MySQL 通过**两阶段提交**保证一致性: ```mermaid graph LR A[事务提交] --> B[写redo log prepare] B --> C[写binlog] C --> D[写redo log commit] ``` 只有 binlog 写入成功后事务才算提交[^2][^3]。 2. **持久化保证** - `FLUSH BINARY LOGS` 强制将 binlog buffer 写入磁盘 - `innodb_fast_shutdown=0` 确保关闭时完成所有 purge 操作[^3] - 虚拟机快照前锁表防止新事务写入 3. **关键参数优化** ```ini sync_binlog=1 # 每次提交同步binlog[^4] innodb_flush_log_at_trx_commit=1 # 每次提交刷新redo log[^1] ``` #### 4. **验证数据一致性** ```sql -- 检查binlogredo log的LSN是否匹配 SHOW ENGINE INNODB STATUS\G -- 查看Last checkpoint at与Log flushed up to差值应<100MB ``` #### 5. **生产环境建议** 1. 使用 Percona XtraBackup 的热备份工具 ```bash xtrabackup --backup --slave-info --safe-slave-backup ``` 2. 在从库执行快照操作,避免影响主库性能 3. 快照完成后立即验证: ```bash mysqlbinlog --verify-binlog-checksum last_binlog_file ``` > **重要提示**:虚拟机快照前必须确保 `sync_binlog=1` `innodb_flush_log_at_trx_commit=1`,否则可能丢失已提交事务[^1][^4]。 --- ### 关键原理总结 | 日志类型 | 持久化机制 | 作用 | |---------|------------|------| | **Redo Log** | `innodb_flush_log_at_trx_commit=1` | 保证事务的持久性[^1] | | **Binlog** | `sync_binlog=1` + `FLUSH BINARY LOGS` | 保证复制数据恢复一致性[^4] | | **协同机制** | 两阶段提交 | 解决 redo/binlog 写入顺序问题[^2] | 通过上述方法,可在**不停止 MySQL 服务**的情况下,确保虚拟机快照时 redo log binlog 达到一致性状态,满足灾备演练需求。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值