日志是MySQL的重要组成部分,其中对于开发而言不得不关注三种重要的日志,分别是二进制日志(bin log)、事务日志(redo log、undo log)。接下来详细介绍这三种日志。
binlog叫做二进制日志,主要是用于记录MySQL表的逻辑变化过程。在实际应用过程中,通常被用于主从复制和数据恢复。
事务执行过程中,会先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。
事务提交后的写入只是写入到文件系统的page cache,并没有把数据持久化到磁盘。持久化磁盘由 *** 作系统决定调用fsync。
MySQL提供了配置决定fsync的时机,当sync_binlog=0的时候,每次提交事务只写入page cache,不执行fsync。当sync_binlog=1的时候,表示每次提交事务都会执行fsync。当sync_binlog = N的时候,每次提交事务都写入page cache,累计多个事务才进行fsync。
显然,当sync_binlog = 1的时候,binlog日志不会丢失。当sync_binlog = N的时候,如果发生异常重启,会丢失N个事务的binlog日志。
STATEMENT
记录数据 *** 作的原始SQL,可能引发主库备库因索引选择不一致,导致数据执行结果不一致。
ROW
ROW基于行复制,只记录哪条数据被修改.缺点:占空间。比如DELETE 语句,对于STATEMENT只占用1条SQL。而ROW格式则需要把所有记录的数据记录下来。
MIXED
对于可能引发主备不一致的命令使用ROW格式,否则使用STATEMTNT
对于每一次更新 *** 作,MySQL都需要写入磁盘,然后需要找到对应那条记录并更新。IO成本较高和查找成本都很高。为了提高性能,MySQL会将更新 *** 作写入redo log,并更新内存。INNODB引擎会在适当的时候将 *** 作记录更新到磁盘。
[图片上传失败...(image-c6a1f2-1627716309698)]
undo log主要是记录了数据的逻辑变化,比如对应一条insear语句,undo log会记录一条delete语方便回退到更新前的值。
时刻A发生故障的话,由于binlog未写入,redo log回滚数据,两个日志数据是一致的。
时刻B发生故障,则需要判断binlog是否完整来决定如何恢复。
redo log和bin log的区别?
为什么redo log crash-safe,而bin log不可以?
MySQL通过内部两阶段提交协议来提交事务,如下图
具体实现如下图:
第一阶段 :InnoDB prepare,持有prepare_commit_mutex,并且write/sync redo log;将rollback设置为Prepared状态,binlog prepare不作任何 *** 作;
第二阶段 :包含两步,write/sync Binlog及 InnoDB commit (写入COMMIT标记后释放prepare_commit_mutex);
考虑mysql以binlog的写入与否作为事务提交成功与否的标志,如果 在写入innodb commit标志时崩溃(binglog已经写文件但是还没有提交) ,则恢复时,会重新对commit标志进行写入;此时的事务崩溃恢复过程如下:
1)扫描最后一个Binlog文件,提取其中的xid;
2)InnoDB维持了状态为Prepare的事务链表,将这些事务的xid和Binlog中记录的xid做比较,如果在Binlog中存在,则提交,否则回滚事务。
但其中也会存在2个问题:
并发危机:全局大锁prepare_commit_mutex
Mysql5.6.5前的做法,加锁,串行化
无锁方案:如果能保证binlog write 和 Innodb commit的顺序一致性就可以解决该问题。
性能问题:参数sync_binlog =1 ,innodb_flush_log_at_trx_commit =1时,fsync *** 作频繁
数据持久化到磁盘:调用fsync将缓存中的数据刷新到磁盘(普通硬盘150次/s和SSD 1200次/S),影响TPS;Group Commit *** 作,在多个事务并发时,将等待fsync的多个事务合并为仅调用一次fsync *** 作,以解决innodb fsync的问题,对binlog 的fsync也适用
对上述两个问题的解决:
针对并发问题
Group *** 作,三个阶段都在维护一个队列。第一个进队列的线程称为leader线程,负责对队列里所有线程进行 *** 作;之后进入队列的线程称作follower线程,follower 线程进入队列后睡眠,等待leader完成 *** 作后将他们唤醒。注意:前一个队列leader进入后一个队列时,会把自己原队列的follower全加入进去。
针对一致性问题
Group commit 分为三个阶段,每个阶段有一个线程在执行。分阶段的目的在于各个阶段可以并发执行,提升效率。
涉及参数说明:
sync_binlog =1 :启用group commit之后,其实已经不是一个事务去刷一次磁盘了,而是一组事务刷一次磁盘。图中1、2分别代表sync_binlog 不同配置下,通知其他线程(如dump线程)binlog 已经更新了,当配置为1时,要严格等到sync完毕之后才会发送广播通知, 如果sync_binlog配的是别的值,MySQL会把通知提前到1的位置
binlog_group_commit_sync_no_delay_count(组提交sync无延迟时间最大event数)及binlog_group_commit_sync_delay(组提交sync延迟时间,单位:毫秒):一般来说我们认为group commit 中最耗时的 *** 作是sync阶段,于是我们可以在sync阶段在leader真正sync之前进行一个等待,以便让fsync一次性刷新更多的事务。这对需要等待sync 完之后才能进行的 *** 作(比如dump线程)可能有性能提升。
两阶段提交:
MYSQL_BIN_LOG作为协调者
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)