15.1 binlog
binlog的作用:
binlog是MySQL serve 层的日志,任何引擎都可以使用。
binlog(归档日志)是以二进制形式保存语句的原始逻辑的,如果单纯依靠binlog,数据库是不具有crash-safe能力的,binlog日志仅仅用于归档,用于日后的数据备份使用。
binlog有两种记录模式,①statement模式:记录的内容是sql语句;②row模式:记录更新前后行的内容,共两条,所以binlog也叫做逻辑日志。
那binlog是以二进制文件的方式保存,那如何查看binlog文件?
- 开启二进制文件,默认为关闭状态,可以
show variables like 'log_bin'
来进行查看。 - 使用sql查看二进制文件
下面是在statement模式下,我们能直观发现日志记录的内容为某表执行某sql语句:
binlog是追加写的,一个文件写到一定大小,就切换下个文件继续写,并不会覆盖以前的日志。
15.2 redo log
redo log的作用:
redo log(重做日志)是属于InnoDB引擎层的日志。
当MySQL进行数据更新操作(insert,update,delete)时,并不会直接更新到磁盘上,而是先写在redo log上,再对内存中的数据进行更新,便算是完成更新操作了,这个技术或者思想叫做WAL:先写日志,后写磁盘。
redo log会在合适的时候将日志中记录的操作一起写到磁盘,,这里从随机IO变成了顺序IO,也减少了IO成本。redo log记录的是这个页面做出了什么改动,例如记录某个表的某个字段从1变成3,又比如是某个物理页的某个偏移位置做了什么操作,是实实在在记录了物理上的变化,所以它叫做物理日志。
什么是redo log合适的机会:
- redo log写满了:出现这种情况,MySQL会停止所有更新操作,等待redo log刷脏页完成。
- 内存满了:根据策略选择淘汰的页面,页面有两种情况:干净页和脏页。干净页无事,直接覆盖使用;脏页则需要将涉及的redo log也更新到磁盘中,使脏页变为干净页,才可继续使用。
- MySQL觉得空闲的时候
- MySQL正常关闭
如何控制刷脏页的性能:
- 告诉MySQL磁盘IO上限有多快:innodb_io_capacity
- 告诉MySQL内存中脏页比例:innodb_man_dirty_pages_pct
- 告诉MySQL支不支持脏页连坐(邻居是脏页一起刷走):innodb_flush_neighbors
redo log存放的内容:
我还没找到查看redo log的方法
redo log的参数设置:
MySQL可以使用如下参数来设置redo log:
- innodb_log_file:每个redo log的大小,默认为48MB
- innodb_log_files_in_group:指redo log文件的数量,默认为2
- innodb_log_group_home_dir:redo log的存放路径,默认为"./",就是MySQL下的data目录
我们可以使用如下命令去查询,红框中就是redo log涉及的参数
我们可以my.ini中对它进行设置,my.ini可以通过cmd+services.msc→服务→MySQL→右键属性得到。
redo log的实际操作:
redo log是循环写的,就已我们上图为为例,我们有两个日志文件,每个文件为48MB,存在两个指针write pos和check point,分别指向写入的位置和擦除的位置,write pos和check point之间是我们可以写入日志的区域。
举例看下图,有write pos和check point两个指针,它们之间的绿色区域是可写入的,蓝色区域是已经存有日志的,当redo log写满时,MySQL会停止一切数据更新操作,将check point前推,check point擦除部分记录的日志操作需要同步到磁盘上,同时内存上内存页也会从脏页变为干净页。
脏页: 内存页上的数据与数据库上的不一致。干净页: 内存上的数据写入磁盘,内存与数据库的数据页内容上一致。
正是因为有了 redo log,MySQL也具有了crash-safe能力,即使MySQL在某个时刻崩溃,之前提交的记录信息也不会丢失。
15.3 两阶段提交
更新语句的执行流程: 例如存在一条sqlupdate t set a = 12,b=10 where id =10
。
- 引擎找到id=10的这一行返回给执行器,在内存中直接返回,不在则加载磁盘再返回。
- 执行器拿到数据,进行修改,调用引擎接口写入这一行。
- 引擎拿到数据更新到内存中,写入redo log,redo log处于prepare状态。
- 执行器生成binlog写入磁盘
- 执行器调用引擎提交事务接口,把刚写入的redo log状态改为commit,更新完成。
两阶段提交的目的是为了保持数据逻辑上的一致性。
我们能假设如果没有两阶段提交,在某个日志写入后,发生系统奔溃,是否还能还原真实数据呢?
先写redo log:数据库实际存在一条,归档日志中少一条,后续如果使用这个binlog来恢复临时库的话,便比实际上缺失了一条。
先写binlog:从奔溃中恢复的数据库会比归档日志少一条更新操作。
不使用两阶段提交,会存在binlog和redo log数据逻辑上不一致的状态。