MySQL的两层架构:
Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数和所有跨存储引擎的功能都在 Server 层实现。
存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎,不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB,从 MySQL 5.5 版本开始, InnoDB 成为了 MySQL 的默认存储引擎。我们常说的索引数据结构,就是由存储引擎层实现的,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+树 ,且是默认使用,也就是说在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引。
这三个日志其中binlog是Server层的,而redolog和undolog是Innodb储存引擎层的。
1、binlog(归档日志)
这个日志记录了所有对数据库的数据、表结构、索引等等变更的操作,不会记录查询类的操作。也就是说只要是对数据库有变更的操作都会记录到binlog里面来。再事务提交后,会将这个事务生成的binlog日志统一写入到binlog日志文件中去。
1.1记录格式:
STATEMENT(默认):每一条修改数据的 SQL 都会被记录到 binlog 中,但 STATEMENT 有动态函数的问题,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;
ROW:记录行数据最终被修改成什么样了,不会出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每行数据的变化结果都会被记录,比如执行批量 update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句而已;
MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;
1.2写入策略:
进行事务的过程中,会先把产生的binlog写入到binlog cache中,事务commit(提交)的时候再写入到日志文件当中,而这个日志文件是否写入磁盘,再根据参数 sync_binlog来决定这个事务的binlog日志是否马上写入到磁盘中。
1、sync_binlog=0 的时候,表示事务结束以后不会立马写入到磁盘当中,只会先写入到日志文件当中,而具体什么是写入磁盘由操作系统决定;
2、sync_binlog=1 的时候,表示每次提交事务都会将日志文件写入到磁盘 ;
3、sync_binlog的值大于1 的时候,表示每次提交事务都先写到page cach,只有等到积累了N个事务之后才写入到磁盘。但这样可能会丢失sync_binlog数的事务日志的风险;
1.3 作用
binlog的主要作用是用于数据复制,再数据库主从架构中,主数据库接受更新请求,而从数据库接收查询请求。这样再加锁时主数据再收写到锁封锁时,从数据库也还能接收查询请求。而保证主从数据库数据一致性用到的就是Binlog日志。它记录了修改主数据库所有修改数据的操作,从数据库只需要按照这个日志就能实现数据复制了。
ps:binlog中记录的是全量数据,所以一旦数据库不小心误删了,也完全可以根据binlog创建出有 个一模一样的数据库。
2、undo log(回滚日志)
undo log记录的是事务开启前的原本的数据,万一事务失败需要恢复到事务执行前的状态,undo log完全可以做到。
执行的是修改语句时,就将这行将要更新行的旧值保存下来带回滚时直接恢复就好。
执行的是新增语句,将新增数据行的主键记录下来,待回滚时根据主键进行删除就好。
执行的时删除语句时,将要删除的一整行数据记录下来,待回滚时再插入进去。
2.1 记录格式
每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id
trx_id记录事务号,表示此时变化是在那个事务中产生的。
roll_pointer 指针则是将这些日志连接起来,也就是所说的版本链。
2.2 写入策略
undo log 和数据页的刷盘策略是一样的,都需要通过 redo log 保证持久化。buffer pool 中有 undo 页,对 undo 页的修改也都会记录到 redo log。redo log 会每秒刷盘,提交事务时也会刷盘,数据页和 undo 页都是靠这个机制保证持久化的。
2.3 作用
undo log的作用有两个。保证事务的原子性,实现回滚操作。配合ReadView实现MVCC。
如果出现了错误或者用户执 行了 ROLLBACK 语句,MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态。
undo log 为每条记录保存多份历史数据,MySQL 在执行快照读(普通 select 语句)的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录。
3、redo log(重做日志)
redo log 是物理日志,记录了某个数据页做了什么修改,比如:对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新,每当执行一个事务就会产生这样的一条或者多条物理日志。在事务提交时,只要先将 redo log 持久化到磁盘即可,可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘。当系统崩溃时,虽然脏页数据没有持久化,但是 redo log 已经持久化,根据 redo log 的内容,将所有数据恢复到最新的状态。这redo log不光记录数据的变化,还记录着undo log的记录变化。
redo log记录的事务完成后的数据新值。事务提交之前发生了崩溃,重启后会通过 undo log 回滚事务,事务提交之后发生了崩溃,重启后会通过 redo log 恢复事务。
修改一个数据的过程是:
先使用undo log记录修改前的值
然后判断缓存(Buffer Pool )中是否有这个数据,如果有则直接修改这个缓存页,并将这个缓存页标志为脏缓存页。
然后通过redo log记录这个修改的操作,并写入磁盘。为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。
待事务提交再生成此次的binlog。
WAL 技术指的是, MySQL 的写操作并不是立刻写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上。
不是说要减少IO嘛,为什么要将redo log写入磁盘,后面还是要将脏数据写入磁盘,多此一举。
写入 redo log 的方式使用了追加操作, 所以磁盘操作是顺序写,而脏数据写入磁盘要先找到数据再磁盘的位置,然后再进行修改,那个是随机写。
顺序写比随机写效率高很多。
3.1 写入格式
r edo log 是为了防止 Buffer Pool 中的脏页丢失而设计的,那么如果随着系统运行,Buffer Pool 的脏页刷新到了磁盘中,那么 redo log 对应的记录也就没用了,这时候我们擦除这些旧记录,以腾出空间记录新的更新操作。redo log 是循环写的方式,相当于一个环形。
3.2写入策略
当然redo log也不是直接写入磁盘当中,他也有缓存,会先写入缓存当中,至于什么是写入磁盘有下面几种情况:
MySQL 正常关闭时;
当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时,会触发落盘;
InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘。
每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘(这个策略可由 innodb_flush_log_at_trx_commit 参数控制,下面会说)。
innodb_flush_log_at_trx_commit=0 ,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;
innodb_flush_log_at_trx_commit=1,表示每次事务提交时都将 redo log 直接持久化到磁盘;
innodb_flush_log_at_trx_commit=2,表示每次事务提交时都只是把 redo log 写到 page cache(操作系统文件缓存)。
3.3 作用
实现事务的持久性,让 MySQL 有 crash-safe (奔溃恢复)的能力,能够保证 MySQL 在任何时间段突然崩溃,重启后之前已提交的记录都不会丢失;
将写操作从「随机写」变成了「顺序写」,提升 MySQL 写入磁盘的性能。