复制的介绍:

根据日志定义的模式不一样,可以分为:Statement(SBR)模式,Row(RBR)格式或者是MIXED格式,记录最小的单位是一个Event,binlog日志前4个字节是一个magic number,接下来19个字节记录Format desc evnet:FDE。MySQL5.6版本增加了GTID复制

下面对三种binlog格式的优缺点:

Statement(基于语句级的复制):

优点:

1)Binlog文件较小

2)日志是包含用户执行的原始SQL,方便统计和审计

3)出现最早binlog兼容较好

4)Binlog方便阅读,方便故障修复

缺点:

1)存在安全隐患,可能导致主从数据不一致

2)对一些系统函数不能准复制或是不能复制,例如

Load_file()

uuid()

User()

Found_rows()

Sysdate()

Row格式(记录了行的修改情况):

优点:

1)相比Statement更安全的复制格式

2)在某些情况下复制速度更快(SQL复杂,表有主键的时候)

3)系统的特殊函数也可以复制

4)更少的锁

缺点:

1)Binary log比较大 (mysql-5.6支持binlog_row_image)

   2)单语句更新或者删除表的行数过多,会形成大量binlog

3)无法从Binlog看见用户执行的SQL (mysql-5.6增加了一个新的Event: binlog_row_query_log_events,记录用户的query)

MIXED格式:

1)混合使用ROW和STATEMENT模式。对于DDL记录会STATEMENT,

2)如果使用innodb表,事务级别使用了READ COMMITED 或READ UNCOMMITTED日志级别只能使用Row格式

3)但在使用ROW格式中的DDL语句还是会记录来STATEMENT格式

4)MIXED模式,那么在以下几种情况下会自动将binlog的模式,由SBR模式改成RBR模式。

1、当DML语句更新一个NDB表时

2、当函数中包含UUID()时

3、2个及以上包含AUTO_INCREMENT字段的表被更新时

4、任何INSERT DELAYED语句时

5、用UDF时

6、视图中必须要求使用RBR时,例如创建视图是使用了UUID()函数

下面我们看幅图,再进行举例子说明:(基于ROW格式的复制流程)

针对这个图,我们以下进行这四种测试:

一、表有主键索引

二、表有唯一键索引

三、表有普通索引

四、表没有任何索引

 测试一:表有主键

在Master上创建tb1表,有主键:

<master>(root@localhost) [xuanzhi]> SHOW variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> show create table tb1\G
*************************** 1. row ***************************
Table: tb1
Create Table: CREATE TABLE `tb1` (
`id` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
`age` tinyint(4) NOT NULL,
`sex` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> select * from tb1;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | innodb | 20 | 0 |
| 20 | myisam | 22 | 1 |
| 30 | memory | 22 | 1 |
| 40 | redis | 32 | 0 |
| 50 | codis | 30 | 1 |
+----+--------+-----+-----+
5 rows in set (0.00 sec) <master>(root@localhost) [xuanzhi]>

在slave上模拟数据不一致,把第一条数据修改下:

Database changed
<slave>(root@localhost) [xuanzhi]> select * from tb1;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | innodb | 20 | 0 |
| 20 | myisam | 22 | 1 |
| 30 | memory | 22 | 1 |
| 40 | redis | 32 | 0 |
| 50 | codis | 30 | 1 |
+----+--------+-----+-----+
5 rows in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> update tb1 set name='innosql' where id=10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 <slave>(root@localhost) [xuanzhi]> select * from tb1;
+----+---------+-----+-----+
| id | name | age | sex |
+----+---------+-----+-----+
| 10 | innosql | 20 | 0 |
| 20 | myisam | 22 | 1 |
| 30 | memory | 22 | 1 |
| 40 | redis | 32 | 0 |
| 50 | codis | 30 | 1 |
+----+---------+-----+-----+
5 rows in set (0.00 sec) <slave>(root@localhost) [xuanzhi]>

这时主从数据已经不一致了,在master执行update操作会不会报1032没有找到记录的错误呢,我们试试:

<master>(root@localhost) [xuanzhi]> update tb1 set name='oracle' where id=10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 <master>(root@localhost) [xuanzhi]>

回Slave库上查看同步是否有问题:

<slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | oracle | 20 | 0 |
+----+--------+-----+-----+
1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.10.132
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000012
Read_Master_Log_Pos: 1632
Relay_Log_File: localhost-relay-bin.000005
Relay_Log_Pos: 1778
Relay_Master_Log_File: mysql-bin.000012
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

可以看到数据是正常复制的,而且同步没有断开。

测试二:有唯一索引

查看表结构,在id列上是有唯一索引的:

<master>(root@localhost) [xuanzhi]> show create table tb1\G
*************************** 1. row ***************************
Table: tb1
Create Table: CREATE TABLE `tb1` (
`id` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
`age` tinyint(4) NOT NULL,
`sex` tinyint(4) NOT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]>

在从库修改一行数据,模拟数据不一致:

<slave>(root@localhost) [xuanzhi]> select * from tb1;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | oracle | 20 | 0 |
| 20 | myisam | 22 | 1 |
| 30 | memory | 22 | 1 |
| 40 | redis | 32 | 0 |
| 50 | codis | 30 | 1 |
+----+--------+-----+-----+
5 rows in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> update tb1 set name='python' where id=10;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0 <slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | python | 20 | 0 |
+----+--------+-----+-----+
1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]>

在Master进行update操作:

<master>(root@localhost) [xuanzhi]> select * from tb1 where id=10;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | oracle | 20 | 0 |
+----+--------+-----+-----+
1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> update tb1 set name='java' where id=10;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0 <master>(root@localhost) [xuanzhi]>

回slave库查看是否出现主从断开的可能:

<slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 10 | java | 20 | 0 |
+----+------+-----+-----+
1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.10.132
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000012
Read_Master_Log_Pos: 2035
Relay_Log_File: localhost-relay-bin.000005
Relay_Log_Pos: 2181
Relay_Master_Log_File: mysql-bin.000012
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

可以看到当主从数据不一到的时候,基于唯一键更新,同步也是正常的,数据也是同步过来的。

测试三:当表只有普通索引

在master上看到表只有普通索引:

<master>(root@localhost) [xuanzhi]> show create table tb1\G
*************************** 1. row ***************************
Table: tb1
Create Table: CREATE TABLE `tb1` (
`id` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
`age` tinyint(4) NOT NULL,
`sex` tinyint(4) NOT NULL,
KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]>

在slave库上模拟数据不一致的情况:

<slave>(root@localhost) [xuanzhi]> update tb1 set name='php' where id=10;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0 <slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 10 | php | 20 | 0 |
+----+------+-----+-----+
1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]>

回到master更新一条记录看看:

<master>(root@localhost) [xuanzhi]> select * from tb1 where id=10;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 10 | java | 20 | 0 |
+----+------+-----+-----+
1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> update tb1 set name='nosql' where id=10;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0 <master>(root@localhost) [xuanzhi]>
<slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 10 | php | 20 | 0 |
+----+------+-----+-----+
1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.10.132
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000012
Read_Master_Log_Pos: 2426
Relay_Log_File: localhost-relay-bin.000005
Relay_Log_Pos: 2369
Relay_Master_Log_File: mysql-bin.000012
Slave_IO_Running: Yes
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 1032
Last_Error: Could not execute Update_rows event on table xuanzhi.tb1; Can't find record in 'tb1', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's master log mysql-bin.000012, end_log_pos 2399

我们查看一下Binlog做了什么操作:

[root@localhost ~]#  mysqlbinlog  -v --base64-output=DECODE-ROWS  --start-position= --stop-position= /data/mysql/data/mysql-bin.
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at
# :: server id end_log_pos Query thread_id= exec_time= error_code=
SET TIMESTAMP=/*!*/;
SET @@session.pseudo_thread_id=/*!*/;
SET @@session.foreign_key_checks=, @@session.sql_auto_is_null=, @@session.unique_checks=, @@session.autocommit=/*!*/;
SET @@session.sql_mode=/*!*/;
SET @@session.auto_increment_increment=, @@session.auto_increment_offset=/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=,@@session.collation_connection=,@@session.collation_server=/*!*/;
SET @@session.lc_time_names=/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at
# at
# :: server id end_log_pos Table_map: `xuanzhi`.`tb1` mapped to number
# :: server id end_log_pos Update_rows: table id flags: STMT_END_F
### UPDATE xuanzhi.tb1
### WHERE
### @=
### @='java'
### @=
### @=
### SET
### @=
### @='nosql'
### @=
### @=
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
[root@localhost ~]#

可以看到,update语句执行的条件大致是这样的:where id=xx and name=xx and age=xx and sex=xx,当这条语句在slave上被执行时,slave库上发现id=10 name=java...这条记录不存在,所以报了1032错误。(后来我看了有主键和唯一索引的binlog日志信息,也是跟普通索引一样的,只是有主键的和唯一索引的时候不会把这条记录的数据都检查一遍)

测试四:表没有索引

在master查看表结构,表是没有索引的:

<master>(root@localhost) [xuanzhi]> show create table tb1\G
*************************** 1. row ***************************
Table: tb1
Create Table: CREATE TABLE `tb1` (
`id` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
`age` tinyint(4) NOT NULL,
`sex` tinyint(4) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> select * from tb1;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | nosql | 20 | 0 |
| 20 | myisam | 22 | 1 |
| 30 | memory | 22 | 1 |
| 40 | redis | 32 | 0 |
+----+--------+-----+-----+
4 rows in set (0.00 sec) <master>(root@localhost) [xuanzhi]>

在slave上修改数据,模拟主从数据不一致:

<slave>(root@localhost) [xuanzhi]> select * from tb1;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | nosql | 20 | 0 |
| 20 | myisam | 22 | 1 |
| 30 | memory | 22 | 1 |
| 40 | redis | 32 | 0 |
+----+--------+-----+-----+
4 rows in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> update tb1 set name='zabbix' where id=10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 <slave>(root@localhost) [xuanzhi]>

回到master库执行update操作:

<master>(root@localhost) [xuanzhi]> update tb1 set name='imysql' where id=10;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0 <master>(root@localhost) [xuanzhi]> select * from tb1;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 10 | imysql | 20 | 0 |
| 20 | myisam | 22 | 1 |
| 30 | memory | 22 | 1 |
| 40 | redis | 32 | 0 |
+----+--------+-----+-----+
4 rows in set (0.00 sec) <master>(root@localhost) [xuanzhi]>

回到slave查看同步情况:

<slave>(root@localhost) [xuanzhi]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.10.132
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000012
Read_Master_Log_Pos: 2914
Relay_Log_File: localhost-relay-bin.000002
Relay_Log_Pos: 346
Relay_Master_Log_File: mysql-bin.000012
Slave_IO_Running: Yes
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 1032
Last_Error: Could not execute Update_rows event on table xuanzhi.tb1; Can't find record in 'tb1', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's master log mysql-bin.000012, end_log_pos 2887

可以看到,当没有索引或者索引是普通索引的时候,Slave SQL_THREAD线程是全表扫描匹配记录,当全表扫描匹配记录时,Relay log在回放的时候发现没有这记录,所以同步断开了。

我们先了解下InnoDB引擎表的一些关键特征:

  • InnoDB引擎表是基于B+树的索引组织表(IOT);
  • 每个表都需要有一个聚集索引(clustered index);
  • 所有的行记录都存储在B+树的叶子节点(leaf pages of the tree);
  • 基于聚集索引的增、删、改、查的效率相对是最高的;
  • 如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择其作为聚集索引;
  • 如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引;
  • 如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入而主键递增,这个ROWID不像ORACLE的ROWID那样可引用,是隐含的)。

综上总结,如果InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的,也就是下面这几种情况的存取效率最高:

  • 使用自增列(INT/BIGINT类型)做主键,这时候写入顺序是自增的,和B+数叶子节点分裂顺序一致;
  • 该表不指定自增列做主键,同时也没有可以被选为主键的唯一索引(上面的条件),这时候InnoDB会选择内置的ROWID作为主键,写入顺序和ROWID增长顺序一致;
  • 除此以外,如果一个InnoDB表又没有显示主键,又有可以被选择为主键的唯一索引,但该唯一索引可能不是递增关系时(例如字符串、UUID、多字段联合唯一索引的情况),该表的存取效率就会比较差。

总结:

一、 每种Binlog格式都有自己的优缺点,如果有数据复制的环境,建议Binlog使用ROW的格式

二、Innodb是索引组织表,建议在设置表的时候都尽管设置有主键,如果没有主键,在插入大数据的时候,会导致严重的主从延时

三、如果表没有主键索引或者唯一键索引,Slave回放Relay log时会全表扫描匹配记录,这速度会非常慢。

参考资料:

http://17173ops.com/2014/09/14/mysql-faq-why-innodb-table-using-autoinc-int-as-pk.shtml?utm_source=tuicool

http://www.cnblogs.com/LMySQL/p/4514554.html

http://imysql.com/node/314

作者:陆炫志

出处:xuanzhi的博客 http://www.cnblogs.com/xuanzhi201111

您的支持是对博主最大的鼓励,感谢您的认真阅读。本文版权归作者所有,欢迎转载,但请保留该声明。

Slave SQL_THREAD如何重放Relay log的更多相关文章

  1. ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository

    salve复制线程停止,尝试start slave 时报ERROR 1872错误mysql> system perror 1872 MySQL error code 1872 (ER_SLAVE ...

  2. Slave failed to initialize relay log info structure from the repository

    现象 查看slave 服务状态 show slave status\G; 错误 Last_Errno: 1872 Last_Error: Slave failed to initialize rela ...

  3. 【故障处理】ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository

    今天在使用冷备份文件重做从库时遇到一个报错,值得研究一下. 版本:MySQL5.6.27 一.报错现象 dba:(none)> start slave; ERROR (HY000): Slave ...

  4. MySQL复制报错(Slave failed to initialize relay log info structure from the repository)

    机器重启以后,主从出现了问题,具体报错信息: Slave failed to initialize relay log info structure from the repository 解决方案: ...

  5. my32_ error 1872 Slave failed to initialize relay log info structure from the repository

    重启了实例后,slave进程无法开启 Last_SQL_Errno: Last_SQL_Error: Slave failed to initialize relay log info structu ...

  6. mysql报错1872: Slave failed to initialize relay log info structure from the repository

    ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository 在一台主机上增加 ...

  7. mysql 主从关系ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository

    连接 amoeba-mysql出现Could not create a validated object, cause: ValidateObject failed mysql> start s ...

  8. mysql5.7启动slave报错 ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository

    原因:检查my.cnf,原来没指定relay_log,mysql默认产生的relay_log名被该server上的另一个mysql slave占用了. 解决方法:1.在my.cnf中添加 relay_ ...

  9. 【FAQ系列】Relay log 导致复制启动失败

    今天在使用冷备份文件重做从库时遇到一个报错,值得研究一下. 版本:MySQL5.6.27 一.报错现象 dba:(none)> start slave; ERROR (HY000): Slave ...

随机推荐

  1. json遍历 分别使用【原】

    json遍历 一 使用org.json.JSONObject遍历 之后的所有遍历都参考了:http://blog.csdn.net/u010648555/article/details/4981538 ...

  2. CentOS6.8下安装redis并配置开机自启动

    参考资料:http://www.bubuko.com/infodetail-1006383.html   http://www.cnblogs.com/skyessay/p/6433349.html ...

  3. 流媒体技术学习笔记之(十七)FFmpeg 3.3《希尔伯特》-新版本的亮点

    FFmpeg 3.3“Hilbert”,一个新的主要版本的一些亮点: 苹果Pixlet解码器 NewTek SpeedHQ解码器 QDMC音频解码器 PSD(Photoshop Document)解码 ...

  4. 堆叠窗口QStackedWidget

    经常将QStackedWidget和QListWidget或者QListView搭配使用 import sys from PyQt5.QtWidgets import QApplication, QW ...

  5. 拆分窗口QSplitter

    拆分窗口中可以添加许多子控件,各个子控件通过拆分线相互分隔开来,拖动该拆分线可以随意改变子控件大小 import sys from PyQt5.QtCore import Qt from PyQt5. ...

  6. 第14月第11天 linkmap

    1. 4.LinkMap服务的开启方式及文件目录 Xcode->Project->Build Settings-> Search map -> 设置 Write Link Ma ...

  7. Python GUI工具Tkinter以及拖拉工具Page安装

    如果使用Tkinter作为Python GUI工具,我们需要安装Tkinter,这个使用conda或者pip即可: conda install -c anaconda tk 为了提高界面编写效率,可以 ...

  8. Java 学习札记(二)TomCat安装配置

    1.下载TomCat 下载地址:http://tomcat.apache.org/ 2.配置环境变量 CATALINA_HOME:F:\JAVA\apache-tomcat-6.0.18\apache ...

  9. mysql一次查询,返回多个统计结果

    1.sum(if) select sum(if(status=1,1,0)) as s1_count,sum(if(status=2,1,0)) as s2_countfrom order; 2.co ...

  10. Java获取资源路径——(八)

     获取文件资源有两种方式: 第一种是: 获取Java项目根目录开始制定文件夹下指定文件,不用类加载器(目录开始要加/) // 获取工程路径 System.out.println(System.getP ...