我们只对前三个进行分析下
binlog是Mysql sever层维护的一种二进制日志,与innodb引擎中的redo/undo log是完全不同的日志;其主要是用来记录对mysql数据更新或潜在发生更新的SQL语句,并以"事务"的形式保存在磁盘中;
主要作用有:
binlog信息查询binlog开启后,可以在配置文件中查看其位置信息,也可以在myslq命令行中查看:
show variables like '%log_bin%';
+---------------------------------+-------------------------------------+
| Variable_name | Value |
+---------------------------------+-------------------------------------+
| log_bin | ON |
| log_bin_basename | /var/lib/mysql/3306/mysql-bin |
| log_bin_index | /var/lib/mysql/3306/mysql-bin.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| sql_log_bin | ON |
+---------------------------------+-------------------------------------+binlog文件开启binlog后,会在数据目录(默认)生产host-bin.n(具体binlog信息)文件及host-bin.index索引文件(记录binlog文件列表)。当binlog日志写满(binlog大小max_binlog_size,默认1G),或者数据库重启才会生产新文件,但是也可通过手工进行切换让其重新生成新的文件(flush logs);另外,如果正使用大的事务,由于一个事务不能横跨两个文件,因此也可能在binlog文件未满的情况下刷新文件
mysql> show binary logs; //查看binlog文件列表,
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 177 |
| mysql-bin.000002 | 177 |
| mysql-bin.000003 | 10343266 |
| mysql-bin.000004 | 10485660 |
| mysql-bin.000005 | 53177 |
| mysql-bin.000006 | 2177 |
| mysql-bin.000007 | 1383 |
+------------------+-----------+查看binlog的状态:show master status可查看当前二进制日志文件的状态信息,显示正在写入的二进制文件,及当前positionmysql> show master status;+------------------+----------+--------------+------------------+-------------------+| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |+------------------+----------+--------------+------------------+-------------------+| mysql-bin.000007 | 120 | | | |+------------------+----------+--------------+------------------+-------------------+reset master 清空binlog日志文件
默认情况下binlog日志是二进制格式,无法直接查看。可使用两种方式进行查看:
a. mysqlbinlog: /usr/bin/mysqlbinlog mysql-bin.000007- mysqlbinlog是mysql官方提供的一个binlog查看工具,- 也可使用–read-from-remote-server从远程服务器读取二进制日志,- 还可使用--start-position --stop-position、--start-time= --stop-time精确解析binlog日志截取位置1190-1352 binlog如下:***************************************************************************************# at 1190 //事件的起点#171223 21:56:26 server id 123 end_log_pos 1190 CRC32 0xf75c94a7 IntvarSET INSERT_ID=2/*!*/;#171223 21:56:26 server id 123 end_log_pos 1352 CRC32 0xefa42fea Query thread_id=4 exec_time=0 error_code=0SET TIMESTAMP=1514123786/*!*/; //开始事务的时间起点 (每个at即为一个event)insert into tb_person set name="name__2", address="beijing", sex="man", other="nothing" //sql语句/*!*/;# at 1352#171223 21:56:26 server id 123 end_log_pos 1383 CRC32 0x72c565d3 Xid = 5 //执行时间,及位置戳,Xid:事件指示提交的XA事务***************************************************************************************b.直命令行解析SHOW BINLOG EVENTS[IN 'log_name'] //要查询的binlog文件名[FROM pos] [LIMIT [offset,] row_count] 1190-135如下:mysql> show binlog events in 'mysql-bin.000007' from 1190 limit 2\G*************************** 13. row ***************************Log_name: mysql-bin.000007Pos: 1190Event_type: Query //事件类型Server_id: 123End_log_pos: 1352 //结束pose点,下个事件的起点Info: use `test`; insert into tb_person set name="name__2", address="beijing", sex="man", other="nothing"*************************** 14. row ***************************Log_name: mysql-bin.000007Pos: 1352Event_type: XidServer_id: 123End_log_pos: 1383Info: COMMIT /* xid=51 */
mysql binlog有三种格式:
row :
日志中会记录成每一行数据被修改的形式,然后在slave端再对相同的数据进行修改,只记录要修改的数据,只有value,不会有sql多表关联的情况。新版本binlog默认为ROW level,且5.6新增了一个参数:binlog_row_image;把binlog_row_image设置为minimal以后,binlog记录的就只是影响的列,大大减少了日志内容
优点:在row模式下,bin-log中可以不记录执行的sql语句的上下文相关的信息,仅仅只需要记录那一条记录被修改了,修改成什么样了,所以row的日志内容会非常清楚的记录下每一行数据修改的细节,非常容易理解。而且不会出现某些特定情况下的存储过程和function,以及trigger的调用和出发无法被正确复制问题。
缺点:在row模式下,所有的执行的语句记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容。
statement :
每一条会修改数据的sql都会记录到master的binlog中,slave在复制的时候sql进程会解析成和原来master端执行多相同的sql再执行。
优点:在statement模式下首先就是解决了row模式的缺点,不需要记录每一行数据的变化减少了binlog日志量,节省了I/O以及存储资源,提高性能。因为他只需要记录在master上所执行的语句的细节以及执行语句的上下文信息。
缺点:在statement模式下,由于他是记录的执行语句,所以,为了让这些语句在slave端也能正确执行,那么他还必须记录每条语句在执行的时候的一些相关信息,也就是上下文信息,以保证所有语句在slave端被执行的时候能够得到和在master端执行时候相同的结果。另外就是,由于mysql现在发展比较快,很多的新功能不断的加入,使mysql的复制遇到了不小的挑战,自然复制的时候涉及到越复杂的内容,bug也就越容易出现。在statement中,目前已经发现不少情况会造成Mysql的复制出现问题,主要是修改数据的时候使用了某些特定的函数或者功能的时候会出现,比如:sleep()函数在有些版本中就不能被正确复制,在存储过程中使用了last_insert_id()函数,可能会使slave和master上得到不一致的id等等。由于row是基于每一行来记录的变化,所以不会出现,类似的问题。
Mixed :
从官方文档中看到,之前的 MySQL 一直都只有基于 statement 的复制模式,直到 5.1.5 版本的 MySQL 才开始支持 row 复制。从 5.0 开始,MySQL 的复制已经解决了大量老版本中出现的无法正确复制的问题。但是由于存储过程的出现,给 MySQL Replication 又带来了更大的新挑战。另外,看到官方文档说,从 5.1.8 版本开始,MySQL 提供了除 Statement 和 Row 之外的第三种复制模式:Mixed,实际上就是前两种模式的结合。在 Mixed 模式下,MySQL 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式,也就是在 statement 和 row 之间选择一种。新版本中的 statment 还是和以前一样,仅仅记录执行的语句。而新版本的 MySQL 中对 row 模式也被做了优化,并不是所有的修改都会以 row 模式来记录,比如遇到表结构变更的时候就会以 statement 模式来记录,如果 SQL 语句确实就是 update 或者 delete 等修改数据的语句,那么还是会记录所有行的变更。
复制是mysql最重要的功能之一,mysql集群的高可用、负载均衡和读写分离都是基于复制来实现的;从5.6开始复制有两种实现方式,基于binlog和基于GTID(全局事务标示符);本文接下来将介绍基于binlog的一主一从复制;其复制的基本过程如下:
Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的 bin-log的 文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的告诉Master从某个bin-log的哪个位置开始往后的日志内容。
Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行
a.配置master主要包括设置复制账号,并授予REPLICATION SLAVE权限,具体信息会存储在于master.info文件中,及开启binlog;mysql> CREATE USER 'test'@'%' IDENTIFIED BY '123456';mysql> GRANT REPLICATION SLAVE ON *.* TO 'test'@'%';mysql> show variables like "log_bin";+---------------+-------+| Variable_name | Value |+---------------+-------+| log_bin | ON |+---------------+-------+查看master当前binlogmysql状态:mysql> show master status;+------------------+----------+--------------+------------------+-------------------+| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |+------------------+----------+--------------+------------------+-------------------+| mysql-bin.000003 | 120 | | | |+------------------+----------+--------------+------------------+-------------------+建表插入数据:CREATE TABLE `tb_person` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(36) NOT NULL, `address` varchar(36) NOT NULL DEFAULT '', `sex` varchar(12) NOT NULL DEFAULT 'Man' ,`other` varchar(256) NOT NULL ,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;insert into tb_person set name="name1", address="beijing", sex="man", other="nothing";insert into tb_person set name="name2", address="beijing", sex="man", other="nothing";insert into tb_person set name="name3", address="beijing", sex="man", other="nothing";insert into tb_person set name="name4", address="beijing", sex="man", other="nothing";b.配置slaveSlave的配置类似master,需额外设置relay_log参数,slave没有必要开启二进制日志,如果slave为其它slave的master,须设置bin_logc.连接mastermysql> CHANGE MASTER TOMASTER_HOST='10.108.111.14',MASTER_USER='test',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000003',MASTER_LOG_POS=120;d.show slave status;mysql> show slave status\G*************************** 1. row ***************************Slave_IO_State: ---------------------------- slave io状态,表示还未启动Master_Host: 10.108.111.14 Master_User: test Master_Port: 20126 Connect_Retry: 60 ------------------------- master宕机或连接丢失从服务器线程重新尝试连接主服务器之前睡眠时间Master_Log_File: mysql-bin.000003 ------------ 当前读取master binlog文件Read_Master_Log_Pos: 120 ------------------------- slave读取master binlog文件位置Relay_Log_File: relay-bin.000001 ------------ 回放binlogRelay_Log_Pos: 4 -------------------------- 回放relay log位置Relay_Master_Log_File: mysql-bin.000003 ------------ 回放log对应maser binlog文件Slave_IO_Running: NoSlave_SQL_Running: NoExec_Master_Log_Pos: 0 --------------------------- 相对于master从库的sql线程执行到的位置Seconds_Behind_Master: NULLSlave_IO_State, Slave_IO_Running, 和Slave_SQL_Running为NO说明slave还没有开始复制过程。e.启动复制start slavef.再次观察slave状态mysql> show slave status\G*************************** 1. row ***************************Slave_IO_State: Waiting for master to send event -- 等待master新的eventMaster_Host: 10.108.111.14Master_User: testMaster_Port: 20126Connect_Retry: 60Master_Log_File: mysql-bin.000003Read_Master_Log_Pos: 3469 ---------------------------- 3469 等于Exec_Master_Log_Pos,已完成回放Relay_Log_File: relay-bin.000002 ||Relay_Log_Pos: 1423 ||Relay_Master_Log_File: mysql-bin.000003 ||Slave_IO_Running: Yes ||Slave_SQL_Running: Yes ||Exec_Master_Log_Pos: 3469 -----------------------------3469 等于slave读取master binlog位置,已完成回放Seconds_Behind_Master: 0可看到slave的I/O和SQL线程都已经开始运行,而且Seconds_Behind_Master=0。Relay_Log_Pos增加,意味着一些事件被获取并执行了。最后看下如何正确判断SLAVE的延迟情况,判定slave是否追上master的binlog:1、首先看 Relay_Master_Log_File 和 Maser_Log_File 是否有差异;2、如果Relay_Master_Log_File 和 Master_Log_File 是一样的话,再来看Exec_Master_Log_Pos 和 Read_Master_Log_Pos 的差异,对比SQL线程比IO线程慢了多少个binlog事件;3、如果Relay_Master_Log_File 和 Master_Log_File 不一样,那说明延迟可能较大,需要从MASTER上取得binlog status,判断当前的binlog和MASTER上的差距;4、如果以上都不能发现问题,可使用pt_heartbeat工具来监控主备复制的延迟。g.查询slave数据,主从一致mysql> select * from tb_person;+----+-------+---------+-----+---------+| id | name | address | sex | other |+----+-------+---------+-----+---------+| 5 | name4 | beijing | man | nothing || 6 | name2 | beijing | man | nothing || 7 | name1 | beijing | man | nothing || 8 | name3 | beijing | man | nothing |+----+-------+---------+-----+---------+
关于mysql复制的内容还有很多,比如不同的同步方式、复制格式情况下有什么区别,有什么特点,应该在什么情况下使用....这里不再一一介绍。
恢复是binlog的两大主要作用之一,接下来通过实例演示如何利用binlog恢复数据:a.首先,看下当前binlog位置mysql> show master status;+------------------+----------+--------------+------------------+-------------------+| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |+------------------+----------+--------------+------------------+-------------------+| mysql-bin.000008 | 1847 | | | |+------------------+----------+--------------+------------------+-------------------+b.向表tb_person中插入两条记录:insert into tb_person set name="person_1", address="beijing", sex="man", other="test-1";insert into tb_person set name="person_2", address="beijing", sex="man", other="test-2";c.记录当前binlog位置:mysql> show master status;+------------------+----------+--------------+------------------+-------------------+| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |+------------------+----------+--------------+------------------+-------------------+| mysql-bin.000008 | 2585 | | | |+------------------+----------+--------------+------------------+-------------------+d.查询数据 mysql> select * from tb_person where name ="person_2" or name="person_1";+----+----------+---------+-----+--------+| id | name | address | sex | other |+----+----------+---------+-----+--------+| 6 | person_1 | beijing | man | test-1 || 7 | person_2 | beijing | man | test-2 |+----+----------+---------+-----+--------+e.删除一条: delete from tb_person where name ="person_2";mysql> select * from tb_person where name ="person_2" or name="person_1";+----+----------+---------+-----+--------+| id | name | address | sex | other |+----+----------+---------+-----+--------+| 6 | person_1 | beijing | man | test-1 |+----+----------+---------+-----+--------+f. binlog恢复(指定pos点恢复/部分恢复)mysqlbinlog --start-position=1847 --stop-position=2585 mysql-bin.000008 > test.sqlmysql> source /var/lib/mysql/3306/test.sqld.数据恢复完成 mysql> select * from tb_person where name ="person_2" or name="person_1";+----+----------+---------+-----+--------+| id | name | address | sex | other |+----+----------+---------+-----+--------+| 6 | person_1 | beijing | man | test-1 || 7 | person_2 | beijing | man | test-2 |+----+----------+---------+-----+--------+e.总结恢复,就是让mysql将保存在binlog日志中指定段落区间的sql语句逐个重新执行一次而已
undo log 和 redo log 其实都不是 MySQL 数据库层面的日志,而是 InnoDB 存储引擎的日志。二者的作用联系紧密,事务的隔离性由锁来实现,原子性、一致性、持久性通过数据库的 redo log 或 redo log 来完成。redo log 又称为重做日志,用来保证事务的持久性,undo log 用来保证事务的原子性和 MVCC。
和大多数关系型数据库一样,InnoDB记录了对数据文件的物理更改,并保证总是日志先行,也就是所谓的wal,既在持久化数据文件前,保证之前的redo日志已经写到磁盘。由于redo log是顺序整块写入,所以性能更好。
重做日志有两部分组成:
redo log记录事务操作的变化,记录的是数据修改之后的值,不管事务是否提交都会记录下来。
在一条语句进行执行的时候,InnoDB引擎会把新记录写到redo log日志中,然后更新内存,更新完成后就算是语句执行完了,然后在空闲的时候或者是按照设定的更新策略将redo log中的内容更新到磁盘。
更详细的步骤,需要了解两个关键词:chechpoint和LSN(log sequence number),前者检查点简单来说就是把脏页刷到磁盘的时间点,这个时间点之前的数据都已经保存到了持久存储。而LSN是InnoDB使用的一个版本标记的计数,它是一个单调递增的值。数据页和redo log都有各自的LSN。每次把redo log中的内容写入到时间的数据页之后,就会把LSN也同步过去。如果发生了宕机,我们可以根据数据页中的LSN值和redo log中的LSN的值判断需要恢复的redo log的位置和大小。redo log同样也有自己的缓存,所以也设计到刷盘策略,是通过innodb_flush_log_at_trx_commit
这个参数控制的。
当对应事务的脏页写入到磁盘之后,redo log 的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。
redo log 的存储都是以块(block)为单位进行存储的,每个块的大小为 512 字节。同磁盘扇区大小一致,可以保证块的写入是原子操作。
另外 redo log 占用的空间是固定的,会循环写入。文件大小由innodb_log_file_size
参数控制。
undo log 有两个作用:提供回滚和多版本并发控制下的读(MVCC)
在数据修改的时候,不仅记录了redo,还记录了相对应的 undo,如果因为某些原因导致事务失败或回滚了,可以借助该 undo 进行回滚。
undo log 和 redo log 记录物理日志不一样,它是逻辑日志。可以认为当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,反之亦然,当 update 一条记录时,它记录一条对应相反的 update 记录。
有时候应用到行版本控制的时候,也是通过 undo log 来实现的:当读取的某一行被其他事务锁定时,它可以从 undo log 中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。
undo log 是采用段(segment)的方式来记录的,每个 undo 操作在记录的时候占用一个 undo log segment。
另外,undo log 也会产生 redo log,因为 undo log 也要实现持久性保护。
当事务提交的时候,InnoDB 不会立即删除 undo log,因为后续还可能会用到 undo log,如隔离级别为 repeatable read 时,事务读取的都是开启事务时的最新提交行版本,只要该事务不结束,该行版本就不能删除,即 undo log 不能删除。
当事务提交之后,undo log 并不能立马被删除,而是放入待清理的链表,由 purge 线程判断是否有其他事务在使用 undo 段中表的上一个事务之前的版本信息,决定是否可以清理 undo log 的日志空间。
在 MySQL 5.7 之前,undo log 存储在共享表空间中,因此有可能大大增加表空间的占用,5.7 之后可以通过配置选择存储在独立的表空间中。
首先 InnoDB 完成一次更新操作的具体步骤: