天天看点

点位、gtid、binlog、redolog

  从库都是通过读取日志的形式来进行“追”主库的备份。在逻辑备份中,日志的记录方式有两种,一种是点位,另一种是gtid。

(1)点位

  点位记录方式是由两部分组成,第一部分是日志的编号,因为假如一个日志的容量是1.1G,当这个日志满了后就会分裂成多个日志,此时从库要从主库的哪一个日志开始读取追赶主库,就要用到第一部分的“位“来定位是哪个日志;第二部分是用来定位日志的事务,也就是从库要执行第几个日志的第几个事务。就是通过这个点位的方式,来实现从库定位到主库读取哪个日志的哪个事务。

但是这里就会出现一个问题,例如:

点位、gtid、binlog、redolog

其中A是主库,B是A的从,C是B的从。在A表中的点位假设要同步的事务点位是0010:107,但是在从库B中,该事务的点位可能就变成其他了,例如变成0010:110(后面讲为什么,主要因为从库的binlog记录是通过relay log来的,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置))。

  此时,B库的relay log去读取A库的binlog的日志内容,然后写到B库的binlog中记录点位,C库也是读取B库的relay log在commit后写到binlog中。因此,如果A库是已经存储的一段时间后的数据库,此时B库去同步A库时,B库的relay log里确实有A库需要进行同步的点位信息,但是当B库的relay log刷新到B库的binlog时,binlog里记录的点位信息记录的就是B库自己的事务点位信息(relay log中的点位的位置),C库同理,所以当B库挂掉后,C库是无法通过binlog和relay log来和A库进行同步的。

  这就是点位复制的一个缺点,因此引出gtid记录方式。

(2)gtid

  gtid(global transaction ID)是对于一个已经提交事务的编号,是由master_uuid组成,是一个全局的唯一编号。这样当出现上面的ABC情况,当B挂掉后,可以通过这个全局的编号让C从库找到A主库要同步的事务。

  逻辑备份其最大的缺陷是备份和恢复速度较慢,如果数据库大于50G,mysqldump备份就不太适合。逻辑备份是备份sql语句,在恢复的时候执行备份的sql语句实现数据库数据的重现。

  在innodb中,他最具有特色的就是redolog日志。

  而对于myisam,他是没有事务的,一般用于系统表,所以当myisam进行主从备份的时候一般是通过锁表的形式,锁表锁1次,直到复制完。

  Innodb中采取的是日志先行,也就是redolog会在实际数据文件修改之前先落地,以保证事务的持久性,所以redolog是同步落地(可能落地内存,这里的内存指的是cache,也可能落地磁盘),binlog是异步落地。之所以这样,是因为当我们主库insert后,要保证数据库的高可用,肯定要持久化,但是面对大量的数据,你去insert或者update开销很大,所以此时不能用数据做到同步,我们去同步的是redolog,让redolog同步落地保证高可用。

  Redolog记录的是数据库所发生的变化,只会针对自己的数据库的数据,不会和从库进行交互。由于redolog的落地是根据策略来选择是同步落地到内存,还是同步落地到磁盘,因此redolog落地到磁盘一般是一个刷的过程。而对于Binlog,他有一个sync_binlog参数来控制数据库的binlog刷到磁盘上去,接下来就讲redolog和binlog的落地过程。

(3)binlog

① sync_binlog=0

当事务提交后,Mysql仅仅是将binlog_cache中的数据写入Binlog文件,但不执行fsync之类的磁盘 同步指令通知文件系统将缓存刷新到磁盘,而让Filesystem自行决定什么时候来做同步,这个是性能最好的。但风险大,因为一旦系统Crash,在binlog_cache中的所有binlog信息都会被丢失。

② sync_binlog=n

在进行n次事务提交以后,Mysql将执行一次fsync之类的磁盘同步指令,同志文件系统将Binlog文件缓存刷新到磁盘,所以风险小,但是性能消耗大。Mysql中默认设置sync_binlog=0,即不作任何强制性的磁盘刷新指令,这时性能是最好的,但风险也是最大的。

binlog的三种模式(row、statement、mixed):

① row:row模式是日志会记录每一行数据的修改,然后再在slave端再对相同的数据进行修改。优点是不记录sql上下文相关信息,会详细的记录下每一行数据修改的细节。但缺点是会产生大量的日志内容,因为每次记录都将以每行记录的修改来记录的,例如我执行一条update,binlog中将不止是记录这条update锁对应的事件,而是这条语句所更新的每一条记录的变化情况。

  例如update product set A=3 where……,那么在日志中会记录update的A=3,B=0,C=0……就是把没有变更的都会记录下来(因为属于变化情况)。

② statement:每一条会修改数据的sql都会记录到master的binlog中,slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql再次执行。他的优点是解决了row的伤痛,不需要记录每一行数据的变化,减少了binlog日志量,但是缺点是为了让这些语句在slave端能正确执行,则还需要记录上下文信息。

③mixed:row和statement的结合,mysql会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在statement和row之间选一个。

(4)redolog

  在InnoDB中,有一个独有的存储层redolog:就是说当我们在编写sql语句的时候,每写一句就会把这一句sql先记录在redolog这个存储层日志里,当我们编写sql为commit之后,redolog就会进入prepare状态,此时才会把redolog里面的内容写到服务层binlog里面,当binlog开始执行到commit后,我们数据库的数据才会更新,然后从库的replay log才去读binlog里面的数据进行同步,这也是innoDB的一个特色。当我们binlog开始执行但是没有执行到commit,说明数据回滚了,那么去回滚的其实并不是binlog,而是redolog,通过redolog的undo去执行回滚。

  Redolog在后面也有提及,无论是物理复制还是逻辑复制,都会有redolog,所谓的日志先行就是指的这个,他以页为单位,记录的是当前页的变化。

O_DIRECT选项:O_DIRECT选项是Linux文件写入中的一个选项,开启了这个选项以后,数据就可以跳过系统层的缓存,直接写入磁盘。但是对于redolog而言,这个选项是关闭的,因此对于redolog,他会根据innodb_flush_log_at_trx_commit 这个参数来控制redolog刷新到磁盘的策略。

① innodb_flush_log_at_trx_commit=1 (默认)

  表示每次事务提交的时候,调用fsync函数来实现redolog的落盘持久化。也就是每次事务提交时,redolog buffer会被写入到日志文件并刷写到磁盘。这也是默认值。这是最安全的配置,但由于每次事务都需要进行磁盘I/O,所以也最慢。

② innodb_flush_log_at_trx_commit=0

  表示事务在执行过程中,日志一直放在redo log buffer中,但是在事务commit的时候,不写入redo log file,而是通过master线程每秒操作一次,从redo log buffer写入到redo log file中然后刷到磁盘。由于跟事务提交无关。在机器crash并重启后,会丢失一秒的事务日志数据。

③ innodb_flush_log_at_trx_commit=2

  每次事务提交的时候,把事务日志数据从redobuffer缓存区写到日志文件redofile中或者操作系统的缓存catch中;每隔一秒,刷新一次日志文件,但不一定刷新到磁盘上,而是取决于操作系统的调度;也就是说,redologfile是由操作系统来调度的,如果为2则他可能会把日志数据存到操作系统缓存中,如果操作系统缓存满了他就刷到磁盘里(这就跟1一样了)。但这里需要注意的是,这里的操作系统缓存和redolog buffer的缓存是两个部分,操作系统缓存由操作系统调度的,只要在操作系统缓存中,Mysql crash就不会丢失操作系统缓存中的数据。

  当取值为 2 时,每次事务提交会写入日志文件,但并不会立即刷写到磁盘,日志文件会每秒刷写一次。这时如果 mysqld 进程崩溃,由于日志已经写入到系统缓存,所以并不会丢失数据;在操作系统崩溃的情况下,通常会导致最后 1s 的日志丢失。所以,当设置为2 的时候,MySQL Crash 并不会造成数据的丢失,但是OS Crash 或者是主机断电后可能丢失的数据量就完全控制在文件系统上了。

  对于一些数据一致性和完整性要求不高的应用,配置为 2 就足够了;如果为了最高性能,可以设置为 0。有些应用,如支付服务,对一致性和完整性要求很高,所以即使最慢,也最好设置为 1。

  上面说的操作系统的缓存其实就是cache,是cpu和内存之间的缓冲,操作系统用cache来提升访问速度,将经常使用的操作结果放到cache中避免总是读取磁盘的操作降低磁盘压力。

  而buffer指的是读写缓冲区,用于内存和硬盘之间的数据交互,就是把要写入硬盘的内容先在内存中堆积,然后攒足后一次性写到硬盘里(也可以直接调用fsync来直接持久化到硬盘,此时BUFFER缓存清空,除此之外linux有一个守护进程定期清空缓冲内容(即写入磁盘)),从而减少磁盘碎片和硬盘的反复寻道。buffer是由各种进程分配的,例如mysql需要则分配buffer给Mysql,当mysql宕机了,那么mysql对应的buffer也没了。

  简单来说,buffer是即将要被写入磁盘的,而cache是被从磁盘中读出来的,cache就相当于落地持久化了,所以上面的innodb_flush_log_at_trx_commit=2的情况,把日志信息要么落地到磁盘redolog file中,要么落地到系统文件缓冲cache中,只要系统不宕机,就不会丢失。Buffer和cache都是都是ram中的数据。