SET @@session.sql_mode=1075838976/!/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/!/;
/!\C utf8mb4 //!/;
SET @@session.character_set_client=45,@@session.collation_connection=45,@@session.collation_server=45/!/;
SET @@session.lc_time_names=0/!/;
SET @@session.collation_database=DEFAULT/!/;
create database test
/!/;
at 313
#190908 21:19:53 server id 1000 end_log_pos 378 CRC32 0xdccfd8ec Anonymous_GTID last_committed=1 sequence_number=2 rbr_only=no
SET @@SESSION.GTID_NEXT= ‘ANONYMOUS’/!/;
at 378
#190908 21:19:53 server id 1000 end_log_pos 497 CRC32 0x57a45727 Query thread_id=2 exec_time=0 error_code=0
use test
/!/;
SET TIMESTAMP=1567991993/!/;
create table gxt1(id int , name varchar(128))
/!/;
at 497
#190908 21:20:17 server id 1000 end_log_pos 562 CRC32 0x316b41e9 Anonymous_GTID last_committed=2 sequence_number=3 rbr_only=yes
/!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED//!/;
SET @@SESSION.GTID_NEXT= ‘ANONYMOUS’/!/;
at 562
#190908 21:20:17 server id 1000 end_log_pos 634 CRC32 0x693ee549 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1567992017/!/;
BEGIN
/!/;
at 634
#190908 21:20:17 server id 1000 end_log_pos 684 CRC32 0xde906285 Table_map: test
.gxt1
mapped to number 108
at 684
#190908 21:20:17 server id 1000 end_log_pos 734 CRC32 0xa0fc1d34 Write_rows: table id 108 flags: STMT_END_F
BINLOG ’
0ah1XRPoAwAAMgAAAKwCAAAAAGwAAAAAAAEABHRlc3QABGd4dDEAAgMPAgACA4VikN4=
0ah1XR7oAwAAMgAAAN4CAAAAAGwAAAAAAAEAAgAC//wBAAAACAB6aGFuZ3NhbjQd/KA=
'/!/;
at 734
#190908 21:20:17 server id 1000 end_log_pos 765 CRC32 0xc9208948 Xid = 10
COMMIT/!/;
at 765
#190908 21:30:18 server id 1000 end_log_pos 812 CRC32 0xda645def Rotate to mysql-bin.000002 pos: 4
SET @@SESSION.GTID_NEXT= ‘AUTOMATIC’ /* added by mysqlbinlog / /!*/;
DELIMITER ;
End of log file
/!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE/;
/!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0/;
上面蓝色字体表示位置,红色表示时间。
SHOW BINARY LOGS ;
SHOW MASTER LOGS ;
show binlog events in ‘mysql-bin.000001’;
SHOW MASTER STATUS;
表示二进制文件已经记录到了第四个,位置为418.
1.5.3、二进制文件的删除
1.reset master将会删除所有日志,并让日志文件重新从000001开始。
mysql> reset master;
2.PURGE { BINARY | MASTER } LOGS { TO ‘log_name’ | BEFORE datetime_expr }
purge master logs to “binlog_name.00000X” 将会清空00000X之前的所有日志文件。例如删除000006之前的日志文件。
mysql> purge master logs to “mysql-bin.000006”;
mysql> purge binary logs to “mysql-bin.000006”;
master和binary是同义词
purge master logs before ‘yyyy-mm-dd hh:mi:ss’ 将会删除指定日期之前的所有日志。但是若指定的时间处在正在使用中的日志文件中,将无法进行purge。
3.使用–expire_logs_days=N选项指定过了多少天日志自动过期清空
1.5.4、相关参数
log-bin=on|off|file_name #或者指定路径log-bin=/data/binlog/mysql-bin表示开启并设定路径,msyql5.7开启该参数必须分配server_id,否则mysql不能启动
server_id=1000 #指定server_id
max_binlog_size: #(默认1G)指定一个二进制文件的最大值,如果超过该值,则产生一个新的二进制日志文件(考虑到事务不可能跨两个binlog文件,所以有时binlog文件的实际大小不会等于设定值)
binlog_cache_size: #(默认32k)存储所有未提交的二进制日志记录,用于缓存,当该事务提交了,则会写入到二进制日志文件中,如果该参数太小,可能)存储所有未提交的二进制日志记录,用于缓存,当该事务提交了,则会写入到二进制日志文件中,如果该参数太小,可能
#会写入到磁盘,影响性能,可以通过查看参数binlog_cache_disk_use,表示写磁盘的次数
sync_binlog: #默认情况下,二进制日志不是每次写都会写入二进制日志文件中,(可以理解为缓冲)如果数据库异常宕机,就会存在二进制日志文件缺少记录的情况,可以通过参数sync_binlog=[N]来进行配置,当N=1时表示同步写磁盘的方式写二进制文件,这是不使用操作系统的缓冲来写日志。
#N=0(默认),虽然设置sync_binlog=1理论上保证了同步,但是当使用innodb存储引擎时,会存在二进制记录了操作,但是事务由回滚了的情况,
#可以通过参数innodb_support_xa=1来解决该问题,innodb_support_xa=1保证了二进制文件和存储引擎文件同步
binlog_do-db: #记录该数据库
binlog_ingnore-db: #忽略该数据库
log_slave-update: #如果当前数据库时复制中的slave角色,不会把从master获取到的二进制日志写入到自己的二进制文件中,如果需要,则需要开启
#这个参数,如果搭建master=>slave=>slave的架构复制,则必须开启此参数
binlog_format: #可以设置为如下的值
#statement:二进制日志文件都是记录的逻辑sql语句
#row:记录表行更改的情况,如果设置为该参数,可以将innodb存储引擎的事务隔离级别基本设置为READ COMMITTED,获得更好的并发,一般设置为row,虽然对磁盘和网络开销大一些,格式为二进制格式。使用mysqlbinlog工具查看
#mixed:mixed模式,默认使用statement配置
expire_logs_days : #指定自动删除二进制日志的时间,即日志过期时间
log_bin_index : #指定mysql-bin.index文件的路径
max_binlog_cache_size : #指定二进制日志缓存最大大小,硬限制。默认4G,够大了,建议不要改
binlog_cache_use:使用缓存写二进制日志的次数(这是一个实时变化的统计值)
binlog_cache_disk_use:使用临时文件写二进制日志的次数,当日志超过了binlog_cache_size的时候会使用临时文件写日志,如果该变量值不为0,则考虑增大binlog_cache_size的值
1.5.5、日志还原
方式一:直接还原
mysqlbinlog --stop-datetime=“2014-7-2 15:27:48” /tmp/mysql-bin.000008 | mysql -u user -p password
方式二:先导入到一个文件然后恢复
mysqlbinlog mysql-bin.000001 > /tmp/a.sql
mysql -u root -p password -e "source /tm
二、事务日志
innodb事务日志包括redo log和undo log。redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作。
undo log不是redo log的逆向过程,其实它们都算是用来恢复的日志:
1.redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。
2.undo用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录
2.1、redo log
redo log主要由重做日志缓冲(redo log buffer)和重做日志文件(redo log file)构成。缓冲数据存于内存,当机器异常或者服务异常会导致缓冲中的数据丢失,日志文件的数据存于磁盘,是持久化文件。
重做日志就是通过先写入缓冲,然后刷新到磁盘的操作,那怎么能够保证数据不会丢失呢?
我们可以通过参数innodb_flush_log_at_trx_commit进行控制,该参数有三个值,分别为0,1,2,在详细了解这个参数之前,我们先了解下重做日志缓冲和重做日志文件的是如何同步的:确保每次日志都能写入到事务日志文件中,在每次将log buffer中的日志写入日志文件的过程中都会调用一次操作系统的fsync操作(即fsync()系统调用)。因为MySQL是工作在用户空间的,MySQL的log buffer处于用户空间的内存中。要写入到磁盘上的log file中(redo:ib_logfile0和ib_logfile1文件,undo:share tablespace或.ibd文件),中间还要经过操作系统内核空间的os buffer,调用fsync()的作用就是将OS buffer中的日志刷到磁盘上的log file中。如下图所示:
接下来继续看 innodb_flush_log_at_trx_commit的值的意义:
当设置为1的时候,事务每次提交都会将log buffer中的日志写入os buffer并调用fsync()刷到log file on disk中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。(推荐)
当设置为0的时候,事务提交时不会将log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到log file on disk中。也就是说设置为0时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。
当设置为2的时候,每次提交都仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk。
redo log是用来数据的crash recovery的,这是数据库保障数据安全的重要功能之一,保存着innodb表中的修改记录,所以也叫日志文件,在innodb存储引擎中,默认有两个日志文件,新建数据库后,会有名为ib_logfile0和ib_logfile1两个文件,如果在启动数据库时,两个文件不存在,则innodb会根据参数创建。
日志文件是以类似循环的方式使用的,当第一个日志文件写满,会往第二个继续写,当所有日志文件写满,则会在第一个日志文件中清空一些空间出来,提供写空间,如下图所示:
innodb中通过日志组来管理日志文件,是一个逻辑定义,它包含若干个日志文件,一组中的日志文件大小相等,大小通过参数设定。
参数innodb_log_files_group 决定,默认值为2,决定每个组有多少个日志文件。
参数innodb_log_group_home_dir,默认文mysql的database目录,决定日志文件存放的目录。
日志组中的每个文件都有自己的格式,内部也是按照大小相同的页面切分,这里的页面大小是512字节,则是机械硬盘块的大小。这样的设计是为了提高数据写入的吞吐量和效率。并且日志将逻辑事务对数据库的分散随机写入转换为512字节整数倍的顺序写入,大大提高数据库写的性能,这也是数据库管理系统区别于文件管理系统的重要因素。
既然说每个日志有很多个512字节的块组成,那么我们了解下每个日志文件的构成,如下图所示:
如上图所示,一个日志文件包含很多个512字节的块,而每个块又分为三部分组成,分别为日志头,日志尾,和具体日志内容。其中日志头占用12字节,日志尾占用8字节,所以剩余的492字节为日志内容占用。
日志头包含4部分:
log_block_hdr_no:(4字节)该日志块在redo log buffer中的位置ID。
log_block_hdr_data_len:(2字节)该log block中已记录的log大小。写满该log block时为0x200,表示512字节。
log_block_first_rec_group:(2字节)该log block中第一个log的开始偏移位置。
lock_block_checkpoint_no:(4字节)写入检查点信息的位置
日志尾只有一个部分: log_block_trl_no ,该值和块头的 log_block_hdr_no 相等
接下来我们看看文件组:在每个组的第一个redo log file中,前2KB记录4个特定的部分,从2KB之后才开始记录log block。除了第一个redo log file中会记录,log group中的其他log file不会记录这2KB,但是却会腾出这2KB的空间。如下:
我们知道重做日志的构成以及其作用之后,那重做日志是什么时间点从缓冲中刷新到日志文件的呢?接下来看看日志刷盘的时机。
日志刷盘时机主要有:
1、log buffer空间用完,
2、master主线程后台每秒刷新一次
3、在DML操作时,会检查日志空间是否足够,如果已经达到预设值,则刷新日志到磁盘
4、checkpoint时,保证所有要刷的数据页中的LSV最小值已经刷入到磁盘,
5、提交逻辑事务时,由参数innodb_flush_log_at_trx_commit 控制
上面提到的LSN称为日志的逻辑序列号(log sequence number),在innodb存储引擎中,lsn占用8个字节。LSN的值会随着日志的写入而逐渐增大。
根据LSN,可以获取到几个有用的信息:
1.数据页的版本信息。
2.写入的日志总量,通过LSN开始号码和结束号码可以计算出写入的日志量。
3.可知道检查点的位置。
实际上还可以获得很多隐式的信息。
LSN不仅存在于redo log中,还存在于数据页中,在每个数据页的头部,有一个fil_page_lsn记录了当前页最终的LSN值是多少。通过数据页中的LSN值和redo log中的LSN值比较,如果页中的LSN值小于redo log中LSN值,则表示数据丢失了一部分,这时候可以通过redo log的记录来恢复到redo log中记录的LSN值时的状态
可以通过:mysql> show engine innodb status\G;查看
其中:
log sequence number就是当前的redo log(in buffer)中的lsn;
log flushed up to是刷到redo log file on disk中的lsn;
pages flushed up to是已经刷到磁盘数据页上的LSN;
last checkpoint at是上一次检查点所在位置的LSN
2.2、undo log
undo log有两个作用:提供回滚和多个行版本控制(MVCC)
在数据修改的时候,不仅记录了redo,还记录了相对应的undo,如果因为某些原因导致事务失败或回滚了,可以借助该undo进行回滚。
undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。
当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。有时候应用到行版本控制的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。undo log是采用段(segment)的方式来记录的,每个undo操作在记录的时候占用一个undo log segment。另外,undo log也会产生redo log,因为undo log也要实现持久性保护
在事务提交的时候,会将该事务对应的undo log放入到删除列表中,未来通过purge来删除。并且提交事务时,还会判断undo log分配的页是否可以重用,如果可以重用,则会分配给后面来的事务,避免为每个独立的事务分配独立的undo log页而浪费存储空间和性能。
通过undo log记录delete和update操作的结果发现:(insert操作无需分析,就是插入行而已)
delete操作实际上不会直接删除,而是将delete对象打上delete flag,标记为删除,最终的删除操作是purge线程完成的。
update分为两种情况:update的列是否是主键列。
如果不是主键列,在undo log中直接反向记录是如何update的。即update是直接进行的。
如果是主键列,update分两步执行:先删除该行,再插入一行目标行
三、事务日志和binlog日志对比
事务日志和binlog区别:
1、记录对象不一样:redo log是在记录存储引擎(innodb)的数据,而二进制文件时记录mysql的数据库更新数据相关的操作,可以是innodb,mymyisam等存储引擎。
2、记录内容不一样:binlog记录的一个事务的具体操作内容,是逻辑日志,redo log记录的是每个页的修改情况,是物理日志。
3、记录时间不一样:binlog仅仅在事务提交前jin进行提交,即只写磁盘一次,而redo log在事务的过程中不断的记录操作,写入redo log中。
4、记录格式不一样:binlog的记录方式和事务的提交顺序有关,而redo log中是记录的物理页的修改,redo log文件中同一个事务可能多次记录,最后一个提交的事务记录会覆盖所有未提交的事务记录。
5、事务日志记录的是物理页的情况,它具有幂等性,因此记录日志的方式极其简练。幂等性的意思是多次操作前后状态是一样的。
binlog和事务日志的先后顺序及group commit:
在提交事务的时候,如果存在修改数据,在提交事务的时候,则需要通过fsync()函数保证重做日志缓冲和重做日志文件的一致性,保证事务的持久性。但是当并发提交很多个事务的时候,就会存在每次提交一个事务,都会调用fsync函数刷新一次磁盘操作,这样的效率比较低,所以innodb存储引擎提供了group commit的功能,可以将多个事务的事务日志通过一次fsync()刷到磁盘中。
提交事务时,在存储引擎层的上一层结构中会将事务按序放入一个队列,队列中的第一个事务称为leader,其他事务称为follower,leader控制着follower的行为。虽然顺序还是一样先刷二进制,再刷事务日志,但是机制完全改变了:保证即使开启了二进制日志,group commit也是有效的。
提交事务主要有如下三个步骤:
flush阶段:向内存中写入每个事务的二进制日志。
sync阶段:将内存中的二进制日志刷盘。若队列中有多个事务,那么仅一次fsync操作就完成了二进制日志的刷盘操作。称为BLGC(binary log group commit)。
commit阶段:leader根据顺序调用存储引擎层事务的提交。
最后
由于文案过于长,在此就不一一介绍了,这份Java后端架构进阶笔记内容包括:Java集合,JVM、Java并发、微服务、SpringNetty与 RPC 、网络、日志 、Zookeeper 、Kafka 、RabbitMQ 、Hbase 、MongoDB、Cassandra 、Java基础、负载均衡、数据库、一致性算法、Java算法、数据结构、分布式缓存等等知识详解。
本知识体系适合于所有Java程序员学习,关于以上目录中的知识点都有详细的讲解及介绍,掌握该知识点的所有内容对你会有一个质的提升,其中也总结了很多面试过程中遇到的题目以及有对应的视频解析总结。
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
2561830)]
本知识体系适合于所有Java程序员学习,关于以上目录中的知识点都有详细的讲解及介绍,掌握该知识点的所有内容对你会有一个质的提升,其中也总结了很多面试过程中遇到的题目以及有对应的视频解析总结。
[外链图片转存中…(img-jwgz9ztW-1725702561831)]
[外链图片转存中…(img-eFeeP26i-1725702561831)]
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0