日志类型 | 写入日志的信息 |
---|---|
二进制日志 | 记录了对MySQL数据库执行更改的所有操作 |
慢查询日志 | 记录所有执行时间超过 long_query_time 秒的所有查询或不使用索引的查询 |
错误日志 | 记录在启动,运行或停止mysqld时遇到的问题 |
通用查询日志 | 记录建立的客户端连接和执行的语句 |
中继日志 | 从复制主服务器接收的数据更改 |
binlog日志是二进制日志(binnary log),以时间形式记录了对MySQL数据库修改的所有操作。
binlog只记录数据库的增删改的操作,查看数据的操作不记录,因为这类操作没有对数据库进行修改的操作。
binlog是mysql server层维护的,跟使用什么引擎没有关系,binlog实在事务最终commit之前写入的。
binlog有两个常用的使用场景:
mysql8中的binLog默认是开启的,5.7默认是关闭的,可以通过参数log_bin
控制。
(1).确认binlog开启
(2).刷新日志以防旧的日志产生影响。
(3)查看所有binlog日志列表:
(4).当我们误删数据库的数据之后,就可以使用mysqlbinlog工具来进行数据恢复,首先需要我们查看binlog的日志文件,因为binlog的日志文件是二进制文件,只能使用mysqlbinlog来查看,查看日志查找到误删操作的位置,执行恢复操作,语句如下:1111为误删操作之前的位置。
/www/server/mysql/bin/mysqlbinlog -v XLW-bin.000126 --stop-position=1111 -v | mysql -uroot -p
*注意当执行恢复操作的时候,会执行这个数据库的一开始的全部操作,如果数据库已经存在,会提示我们数据库已经存在,当然我们也可以指定范围来恢复数据。
# 指定位置范围
/usr/bin/mysqlbinlog -v XLW-bin.000126 --start-position=0 --stop-position=986
# 指定时间范围
/usr/bin/mysqlbinlog -v XLW-bin.000126 --start-datetime="2023-12-22 11:18:00" --stop-datetime="2023-12-22 12:18:00"
binlog 有三种格式, 使用变量binlog_format
查看当前使用的是哪一种:
Statement和Row的优劣比较:
常见的事件类型有:
二进制日志文件并不是每次写的时候同步到磁盘。因此当数据库所在操作系统发生宕机时,可能会有最后一部分数据没有写入二进制日志文件中,这给恢复和复制带来了问题。 参数sync_binlog=[N]
表示每写多少次就同步到磁盘。如果将N设为1,即sync_binlog=1表示采用同步写磁盘的方式来写二进制日志,这时写操作不使用操作系统的缓冲来写二进制日志。(备注:该值默认为0,采用操作系统机制进行缓冲数据同步)。
略
MySQL通用查询日志,它是记录建立的客户端连接和执行的所有DDL和DML语句(不管是成功语句还是执行有错误的语句),默认情况下,它是不开启的。请注意,它也是一个文本文件。
慢查询日志就是记录我们执行sql语句时,那些比较慢的数据,也就是超过我们设定的查询时间的sql语句,通过以下语句可以查看设定的时间:
错误日志(Error Log)主要记录 MySQL 服务器启动和停止过程中的信息、服务器在运行过程中发生的故障和异常情况等。即使是MySQL服务器启动失败,该日志也会记录错误原因。
redo log(重做日志)的设计主要是为了防止因系统崩溃而导致的数据丢失,其实解决因系统崩溃导致数据丢失的思路如下:
1、每次提交事务之前,必须将所有和当前事务相关的【buffer pool中的脏页】刷入磁盘,但是,这个效率比较低,可能会影响主线程的效率,产生用户等待,降低响应速度,因为刷盘是I/O操作,同时一个事务的读写操作也不是顺序读写。
2、把当前事务中修改的数据内容在日志中记录下来,日志记录是顺序写,性能很高。其实mysql就是这么做的,这个日志被称为redo log。执行事务中,每执行一条语句,就可能有若干redo日志,并按产生的顺序写入磁盘,redo日志占用的空间非常小,当redo log空间满了之后又会从头开始以循环的方式进行覆盖式的写入。
redo log的格式比较简单,包含一下几个部分:
type:该日志的类型,在5.7版本中,大概有53种不同类型的redo log,占用一个字节
space id:表空间id
page number:页号
data:日志数据
在innodb执行任务时,有很多操作,必须具有原子性,我们把这一类操作称之为MIni Transaction。
在我们向B+树中插入一条记录的时候,需要定位这条数据将要插入的【数据页】,因为插入的位置不同,可能会有以下情况:
1、待插入的页拥有【充足的剩余空间】,足以容纳这条数据,那就直接插入就好了,这种情况需要记录一条【MLOG_COMP_REC_INSERT类型】的redo日志就好了,这种情况成为乐观插入。
2、待插入的页【剩余空间不足】以容纳该条记录,这样就比较麻烦了,必须进行【页分裂】了。必须新建一个页面,将原始页面的数据拷贝一部分到新页面,然后插入数据。这其中对应了好几个操作,必须记录多条rede log,包括申请新的数据页、修改段、区的信息、修改各种链表信息等操作,需要记录的redo log可能就有二三十条,但是本次操作必须是一个【原子性操作】,在记录的过程中,要全部记录,要么全部失败,这种情况就被称之为一个MIni Transaction(最小事务)。
(1).MTR的按组写入
对于一个【MTR】操作必须是原子的,为了保证原子性,innodb使用了组的形式来记录redo 日志,在恢复时,要么这一组的的日志全部恢复,要么一条也不恢复。innodb使用一条类型为MLO_MULTI_REC_END
类型的redo log作为组的结尾标志,在系统崩溃恢复时只有解析到该项日志,才认为解析到了一组完整的redo log,否则直接放弃前边解析的日志。
(2).单条redolog的标识方法
有些操作只会产生一条redo log,innodb是通过【类型标识】的第一个字符来判断,这个日志是单一日志还是组日志,如下图:
(3).事务、sql、MTR、redolog的关系如下
任何可能产生大量I/O的操作,一般情况下都会设计【缓冲层】,mysql启动时也会向操作系统申请一片空间作为redo log的【缓冲区】,innodb使用一个变量buf_free
来标记下一条redo log的插入位置(标记偏移量),log buffer会在合适的时机进行刷盘:
innodb_log_buffer_size
指定,当写入log buffer的日志大于容量的50%,就会进行刷盘。redolog日志文件容量是有限的,需要循环使用,redo log的作用仅仅是为了在崩溃时恢复脏页数据使用的,如果脏页已经刷到磁盘上,其对应的redo log也就没用了,他也就可以被重复利用了。checkpoint的作用就是用来标记哪些旧的redo log可以被覆盖。
我们已经知道,判断redo log占用的磁盘空间是否可以被重新利用的标志就是,对应的脏页有没有被刷新到磁盘。为了实现这个目的,我们需要了解一下下边几个记录值的作用:
(1)lsn
lsn(log sequence number)是一个全局变量。mysql在运行期间,会不断的产生redo log,日志的量会不断增加,innodb使用lsn来记录当前总计写入的日志量,lsn的初始值不是0,而是8704,原因未知。系统在记录lsn时是按照【偏移量】不断累加的。lsn的值越小说明redo log产生的越早。
每一组redo log都有一个唯一的lsn值和他对应,可以理解为lsn是redo log的年龄。
(2)flush_to_disk_lsn
flush_to_disk_lsn
也是一个全局变量,表示已经刷入磁盘的redo log的量,他小于等于lsn,举个例子:
1、将redo log写入log buffer,lsn增加,假如:8704+1024 = 9728,此时flush_to_disk_lsn不变。
2、刷如512字节到磁盘,此时flush_to_disk_lsn=8704+512=9256。
如果两者数据相同,说明已经全部刷盘。
(3)flush链中的lsn
其实要保证数据不丢失,核心的工作是要将buffer pool中的脏页进行刷盘,但是刷盘工作比较损耗性能,需要独立的线程在后台静默操作。
回顾一下flush链,当第一次修改某个已经加载到buffer pool中的页面时,他会变成【脏页】,会把他放置在flush链表的头部,flush链表是按照第一次修改的时间排序的。在第一次修改缓冲页时,会在【缓冲页对应的控制块】中,记录以下两个属性:
既然flush链表是按照修改日期排序的,那么也就意味着,oldest_modification的值也是有序的。
(4)checkpoint过程
执行一个check point可以分为两个步骤
**第一步:**计算当前redo log文件中可以被覆盖的redo日志对应的lsn的值是多少:
1、flush链是按照第一次修改的时间排序的,当然控制块内的【oldest_modification】记录的lsn值也是有序的。
2、我们找到flush链表的头节点上的【oldest_modification】所记录的lsn值,也就找到了一个可以刷盘的最大的lsn值,小于这个值的脏页,肯定已经刷入磁盘。
3、所有小于这个lsn值的redo log,都可以被覆盖重用。
4、将这个lsn值赋值给一个全局变量checkpoint_lsn,他代表可以被覆盖的量。
**第二步:**将checkpoint_lsn与对应的redo log日志文件组偏移量以及此次checkpoint的编号(checkpoint_no也是一个变量,记录了checkpoint的次数)全部记录在日志文件的管理信息内。
(1)log buffer中的日志丢失,log buffer中的日志会在每次事务前进行刷盘,如果在事务进行中崩溃,事务本来就需要回滚。
(2)buffer pool中的脏页丢失,崩溃后可以通过redo log恢复,通过checkpoint操作,我们可以确保,内存中脏页对应的记录都会在redo log日志中存在。
redo log保证了崩溃后,数据不丢失,但是一个事务进行中,如果一部分redo log已经刷盘,那么系统会将本应回滚的数据同样恢复,为了解决回滚的问题,innodb提出了undo log。
undo log(也叫撤销日志或者回滚日志),他的主要作用是为了实现回滚操作。同时,他是MVCC多版本控制的核心模块。undo log保存在共享表空间【ibdata1文件】中。
*8.0以后undolog有了独立的表空间
在innodb的行数据中,会自动添加两个隐藏列,一个是【trx_id】,一个是【roll_pointer】,如果该表中没有定义主键,也没有定义【非空唯一】列,则还会生成一个隐藏列【row_id】,是为了生成聚簇索引使用的。
小知识:
undo log在记录日志时是这样记录的,每次修改数据,都会将修改的数据标记一个【新的版本】,同时,这个版本的数据的地址会保存在修改之前的数据的roll_pointer列中。
当我们对数据库的数据进行一个操作时必须记录之前的信息,将来才能回滚,如下:
innodb将undo log分为两类:
什么分为这两类呢?
undo同样是以页的形式进行存储的,多个页是使用链表的形式进行管理,针对【普通表和临时表】,【插入型和修改型】的数据。
开启事务,执行【增删改】时获得【事务id】。
在系统表空间中第5号页中,分配一个回滚段,回滚段是轮动分配的,比如,当前事务使用第5个回滚段,下个事务就使用第6个。
【回滚段】是一个【数据页】,里边划分了1024个undo slot,用来存储日志链表的头节点地址。
在当前回滚段的cached链表(回收可复用的)和空闲solt中,找到一个可用的slot,找不到就报错。
创建或复用一个undo log页,作为first undo page,并把他的地址写入undo solt中。