转载本文请联系程序员内电史公众号。在进入正题之前,我们先简单了解一下MySQL的逻辑架构。我相信我可以使用它。MySQL逻辑架构MySQL的逻辑架构大致可以分为三层:第一层:处理客户端连接、授权认证、安全验证等第二层:Server服务器层,负责SQL的解释、分析、优化、执行运算引擎等第三层:存储引擎,负责MySQL中数据的存储和提取。我们要知道MySQL的服务器层是不管理事务的。事务是由存储引擎实现的,而MySQL中支持事务的存储引擎是使用最广泛的InnoDB,所以后续文章中提到的存储引擎都是基于InnoDB的。记住MySQL的数据更新过程!记住!记住!上图展示了MySQL更新数据的基本过程,包括redolog、binlog、undolog的大致关系。直行。重做日志(redolog)重做日志属于MySQL存储引擎InnoDB的事务日志。MySQL数据存储在磁盘上,每次读写数据都需要进行磁盘IO操作。如果数据是并发的,性能会很差。为此,MySQL提供了一种优化方法,引入了缓存缓冲池。这个缓存包含了一些数据页(pages)在磁盘中的映射,从而缓解数据库的磁盘压力。从数据库中读取数据时,先从缓存中读取,如果不在缓存中,则从磁盘中读取并放入缓存中;向数据库写入数据时,先写入缓存,此时缓存中的数据页数据发生变化。此数据页称为脏页。BufferPool中的数据被修改后,会按照设定的更新策略周期性的刷新到磁盘中。这个过程称为刷新脏页。如果MySQL宕机,脏页还没有被flush,可能是因为某些原因MySQL重启了。此时BufferPool中修改的数据还没有及时刷入磁盘,会导致数据丢失,无法保证事务的持久性。.为了解决这个问题,引入了redolog。redolog顾名思义,专注于重做!它记录了数据库中每一页的修改情况,而不是某一行或某几行是如何被修改的,可以用来恢复提交数据页后的物理状态,只能恢复到最后一次提交的位置。重做日志使用了WAL(Write-AheadLogging)技术。该技术的核心是在修改记录之前,必须先写入日志,必须将日志写入磁盘,才能认为事务完成。当用redolog修改数据时,InnoDB引擎会先在redolog中写入更新记录,然后再修改BufferPool中的数据。提交事务时,调用fsync将重做日志刷新到磁盘。至于缓存中更新的数据文件何时刷新到磁盘,由后台线程异步处理。注意:此时redolog的事务状态为prepare,还没有commit成功完成。直到binlog日志写入磁盘后才会改为commit,才认为事务已提交。这样即使在刷脏页之前MySQL意外崩溃也没关系,只要在重启时解析重做日志中的变化记录并重放,再次刷新磁盘即可。固定大小重做日志采用固定大小、循环写入的格式。当redolog满了,就从头开始,再次循环写入,形成一个环。那为什么要这样设计呢?因为redolog记录的是datapage上的修改,如果BufferPool中的datapage已经flush到磁盘,那么这些记录就会失效,新的log会覆盖擦除这些失效记录。上图中的writepos表示redolog中当前记录的日志序号LSN(logsequencenumber),还没有写入磁盘,循环递增;checkpoint表示redolog中的修改记录被flush到磁盘后的LSN,循环往后递增,这个LSN之前的数据都已经放到磁盘上了。writepos和checkpoint之间的部分是redolog的空闲部分(绿色),用来记录新的日志;checkpoint和writepos之间的部分是已经记录在redolog中的数据页的修改数据。此时,数据页还没有Flush回磁盘的部分。当writepos追上checkpoint时,会先将checkpoint向前推清空位置(刷盘),然后记录新的log。注意:重做日志已满。擦除前需要确保内存中待擦除记录对应的数据页已经刷入磁盘。在擦除旧记录腾出新空间期间,收不到新的更新请求,此时MySQL的性能会下降。因此,在并发量较大的情况下,合理调整redolog的文件大小非常重要。Crash-safe由于redolog的存在,Innodb引擎具有crash-safe的能力,即当MySQL宕机重启时,系统会自动检查redolog,恢复未被修改的数据从重做日志写入磁盘到MySQL。MySQL在启动时,无论上次是正常关闭还是异常关闭,都会进行一次恢复操作。它会首先检查数据页中的LSN。如果LSN小于redolog中的LSN,即writepos位置,则说明数据页上未完成的操作记录在redolog中,然后会从最近的checkpoint开始。开始同步数据。简单理解,例如:redolog的LSN为500,datapage的LSN为300,说明重启前部分数据还没有完全刷入磁盘,那么系统会重放LSN号的记录300到500在redolog刷盘。undolog(回滚日志)undolog也是属于MySQL存储引擎InnoDB的事务日志。undolog属于逻辑日志,顾名思义主要起到回滚的作用,是保证事务原子性的关键。记录的是数据修改前的状态。在数据修改过程中,undolog中会同时记录一条与当前操作相反的逻辑日志。举个栗子:如果更新ID=1记录的name字段,name原来的数据是Xiaofu,现在把name改成程序员内部事务。在执行updateXsetname=programmer'sinternalaffairswhereid=1语句时,首先会在undolog中记录一条updateXsetname=Xiaofuwhereid=1逻辑相反的记录,这样当某些原因导致异常时服务事务失败,可以使用undolog将数据回滚到事务执行前的状态,保证事务的完整性。那么有人可能会问:同一个事物中的一条记录被修改了多次,是否需要每次都将数据修改前的状态写入到undolog中?答案是不!undolog只负责记录事务在开始之前,我们需要修改原始版本的数据。当我们再次修改这行数据时,生成的修改记录会被写入redolog。undolog负责完成回滚,redolog负责完成前滚。回滚一个未提交的事务,即事务还没有执行commit。但是,在这个事务中修改的脏页中,有一些脏块可能已经被flush掉了。如果此时数据库实例崩溃重启,则需要使用rollback来撤销之前部分已经从磁盘中flush掉的脏块。前滚未完全提交的事务,即事务已经执行了commit,但是在事务中修改的脏页中只有一部分数据被flush,另一部分还在bufferpoolcache中。如果此时数据库实例宕机重启,则需要使用前滚来完成未完全提交的事务。以后由于内存宕机,从redolog中恢复数据并刷入磁盘。恢复数据库实例时,先前滚,再回滚。如果你仔细阅读了上面的MySQL数据更新流程图,你会发现undolog、redolog、binlog这三种日志,在脏页被flush之前,都是先flush到磁盘的。相互合作确保最大程度地保护用户。提交的数据不会丢失。binlog(归档日志)binlog是数据库服务器层(不管是什么引擎)以二进制形式存储在磁盘上的逻辑日志。bin日志记录了数据库的所有DDL和DML操作(不包括SELECT和SHOW等命令,因为此类操作不会修改数据本身)。默认情况下,二进制日志记录是禁用的。可以使用如下命令查看二进制日志是否开启:mysql>SHOWVARIABLELIKE'log_bin';+-------------+--------+|Variable_name|值|+----------------+--------+|log_bin|OFF|+----------------+-------+binlog也叫archivelog,因为它不会像redolog那样循环写入和擦除以前的记录,而是会一直记录日志。一个binlog日志文件默认最大容量为1G(也可以通过max_binlog_size参数修改)。如果单个日志超过最大值,将创建一个新文件继续写入。mysql>showbinarylogs;+----------------+----------+|Log_name|File_size|+---------------+--------+|mysq-bin.000001|8687||mysq-bin.000002|1445||mysq-bin.000003|3966||mysq-bin.000004|177||mysq-bin.000005|6405||mysq-bin.000006|177||mysq-bin.000007|154||mysq-bin.000008|154|bin日志的内容格式其实就是执行SQL命令的逆向逻辑,有点类似于undolog。一般来说,打开bin日志会设置日志文件的过期时间(expire_logs_days参数,默认永久保存),否则日志的体积会很大。mysql>showvariablelike'expire_logs_days';+----------------+--------+|Variable_name|Value|+------------------+--------+|expire_logs_days|0|+----------------+-------+1rowinsetmysql>SETGLOBALexpire_logs_days=30;QueryOK,0rowsaffectedbin日志主要用于MySQL主从模式(master-slave),主从节点之间的数据同步;以及基于时间点的数据恢复。主从同步通过下图MySQL的主从复制过程,我们可以了解binlog在主从模式下的应用。用户在主库master上执行DDL和DML操作,将记录序列写入binlog;从库slave的I/O线程连接Master,请求读取指定位置的日志内容;Master收到从库slave的请求后,向从库推送指定位置后的日志内容,主库的bin日志文件名,日志中的位置;slave的I/O线程接收到数据后,将接收到的日志内容写入relaylog文件的末尾,并将读取到的master库bin日志文件名和位置记录到master-info文件中,以供后续使用下一个阅读;Slave的SQL线程检测到relaylog中的内容有更新后,读取Log并解析成可执行的SQL语句,从而实现主从数据库的数据一致性;基于时间点恢复,我们可以看到binlogs也可以用于数据恢复,redologs也可以,那么它们有什么区别呢?层次结构不同:redolog由InnoDB存储引擎实现,binlog由MySQL的server层实现,但MySQL数据库中的任何存储引擎都会对数据库的变化产生binlog。作用不同:redolog用于崩溃恢复(crashrecovery),保证MySQL宕机不会影响持久化;binlog用于时间点恢复(point-in-timerecovery),保证服务器可以恢复数据和Master-slave复制。内容不同:redolog是物理日志,内容以磁盘的pagepage为准;binlog的内容是二进制的,可以根据binlog_format参数设置。写法不同:redolog是循环写记录的;binlog是通过append的方式记录的。当文件大小大于给定值时,后续日志将记录到新文件中。flush的时机不同:binlog是在事务commit的时候写的;事务开始时写入重做日志。binlog和redolog的功能并不冲突,而是相辅相成。它们需要同时记录下来,以保证数据库在宕机重启时不会丢失数据。中继日志(relaylog)中继日志日志文件与binlog日志文件格式相同。从上面的MySQL主从复制过程可以看出,relaylog起到了中转的作用,slave先从主库读取master的二进制日志数据写入从库本地,然后异步读取并由SQL线程通过relaylog进行解析,执行相应的SQL命令。slowquerylog慢查询日志(slowquerylog):用于记录MySQL中执行时间超过指定时间的查询语句,常用于SQL优化过程中。通过慢查询日志,我们可以找出哪些查询语句是低效耗时的。出于性能考虑,一般只有在排查慢SQL和调试参数时才启用。慢查询日志功能默认关闭。可以使用如下命令查看慢查询日志是否开启:mysql>SHOWVARIABLELIKE'slow_query%';+--------------------+--------------------------------------------------------+|变量名|值|+--------------------+--------------------------------------------------+|slow_query_log|OFF||slow_query_log_file|/usr/local/mysql/data/iZ2zebfzaequ90bdlz820sZ-slow.log|+--------------------+-------------------------------------------------------+通过以下命令打开慢查询日志后,发现iZ2zebfzaequ90bdlz820sZ-slow.log日志文件中没有内容,可能是因为我执行的SQL比较简单,没有超过规定的时间。mysql>SETGLOBALslow_query_log=ON;QueryOK,0rowsaffected上面提到超过指定时间的查询语句被认为是慢查询,那么时间阈值是多少呢?我们通过long_query_time参数查看,发现默认是10秒。mysql>SHOWVARIABLELIKE'long_query_time';+----------------+----------+|Variable_name|Value|+----------------+------------+|long_query_time|10.000000|+----------------+----------+这里我们将long_query_time参数修改为0.001秒,再次执行查询SQL,查看慢查询日志是否有变化。mysql>SETGLOBALlong_query_time=0.001;QueryOK,0rowsaffected果然,再次执行SQL时,执行时间大于0.001秒,发现慢查询日志开始记录了。SlowqueryloggeneralquerylogGeneralquerylog(通用查询日志):用于记录所有用户操作,包括客户端连接服务器时,客户端发送的所有SQL,以及其他事件,比如MySQL服务的启动和关闭,ETC。。MySQL服务器按照收到语句的顺序写入日志文件。因为一般查询日志记录的内容过于详细,开启后日志文件的体积会很大。所以,出于性能的考虑,日志功能默认是关闭的,一般在排错的时候需要获取详细的日志,才会临时开启。我们可以使用如下命令查看是否启用了通用查询日志,命令如下:mysql>showvariableslike'general_log';+----------------+-------+|变量名|值|+----------------+--------+|general_log|OFF|+----------------+-------+打开下面的通用查询日志,查看日志存放的位置。mysql>SETGLOBALgeneral_log=on;QueryOK,0rowsaffectedmysql>showvariableslike'general_log_file';+----------------+--------------------------------------------------+|Variable_name|Value|+------------------+-----------------------------------------------+|general_log_file|/usr/local/mysql/data/iZ2zebfzaequ90bdlz820sZ.log|+----------------+------------------------------------------------+执行一条查询SQL,查看日志内容的变化。mysql>select*fromt_config;+----------------+------------+----------------------+--------------------+|id|remark|create_time|last_modify_time|+--------------------------+------------+--------------------+-------------------+|1325741604307734530|我是电波手表|2020-11-0918:06:44|2020-11-0918:06:44|+--------------------+------------+---------------------+--------------------+我们看到日志内容记录了所有执行过的命令,SQL,详细的SQL解析过程,数据库设置等一般查询日志错误日志错误日志(errorlog):应该是MySQL中最好理解的日志了。主要记录MySQL服务器每次启动和停止的时间,以及诊断和错误信息。默认情况下,日志功能是开启的。使用以下命令查找错误日志文件的存放路径。mysql>SHOWVARIABLELIKE'log_error';+----------------+----------------------------------------------------------------+|Variable_name|Value|+--------------+------------------------------------------------------------------+|log_error|/usr/local/mysql/data/LAPTOP-UHQ6V8KP.err|+--------------+----------------------------------------------------------------+注意:并不是所有的错误信息都记录在错误日志中,比如MySQL如何启动InnoDB表空间文件,如何初始化自己的存储引擎,初始化缓冲池等,也记录在错误日志文件中。综上所述,MySQL是我们工作中遇到频率最高的中间件。熟练使用它只是一个介绍。要想在简历中写得熟练,就需要深入了解其内部工作原理,而这7类日志只是在深入学习过程中。一个起点,学无止境,哥的功课完了!
