一、基于语句的复制
在MySQL5.0之前的版本只支持基于语句的复制(也称为逻辑复制),这在数据库领域是很少见的。基于语句的复制模式下,主库会记录那些造城数据更改的查询,当备库读取并重放这些事件时,实际上只是将主库上的SQL再执行一遍。 这种方式的既有好处,又有缺点。
最明显的好处就是实现简单。理论上讲,简单的记录和执行这些语句,能够让主备保持同步。另一个好处是二进制日志文件里的事件更加紧凑,所以相对而言,基于语句的模式不会使用太多带宽。一条更新好几兆的语句在二进制日志文件里可能只占用几十个字节。另外使用 mysqlbinlog 工具是使用基于语句的日志的最佳工具。
但事实上基于语句的复制可能并不如此便利。因为主库上的数据更新除了执行的语句外,可能还依赖于其他因素。例如,同一条SQL在主库和备库上执行的时间可能稍微不同,因此在传输的二进制日志中,除了查询语句,还包括了一下元数据信息,如当前的时间戳。即便如此,还存在一些无法被正确复制的SQL。例如,使用CURRENT_USER() 函数的语句。存储过程和触发器在使用基于语句的复制也可能存在问题。
二、基于行的复制
MySQL5.1开始支持基于行的复制,这种方式会将实际数据记录在二进制日志中,跟其他数据库比较相像。它有其自身的一些优点和缺点。最大的好处是可以正确的复制每一行,一些语句可以被更加有效地复制。
由于无法重放更新主库数据的查询,使用基于行的复制模式能够高效地复制数据。重放一些查询的语句代价可能会更高。例如:
mysql> INSERT INTO table1 (col1,c ol2, col3) -> SELECT col, col2, sum(col3) -> form table2 -> GROUP BY col1, col2;
如果表 table1 的列 col1, col2有三种组合,这个查询可能会在源表上扫描多次,但最终只会在目标表上产生三行数据。但基于行的复制方式,在备库上的开销可能会小很多。这种情况下,使用基于行的复制更加高效。
但在另一方面,下面的sql语句使用基于语句的复制方式代价会小很多:
msyql> update table set col1 = 1;
由于做了全表更新,使用基于行的复制开销会很大。
由于没有哪种模式对所有的情况都是完美的,MySQL能够在这两种模式间动态切换。默认情况下使用的是基于语句的复制方式,但如果发现语句无法被正确复制,就切换到基于行的复制。
对于基于行的复制,很难进行时间点的恢复,但这并非不可能。可以利用日志服务器进行恢复。
三、基于行或基于语句:哪种更优
理论上,基于行的模式整体上更优,并且在实际应用中也适合大多数场景。以下详细阐述两种方式的优点和确定。
- 基于语句的复制模式的优点
当主备的模式不同时,逻辑能够在大多数情况下工作。例如,在主备上的表的定义不同但数据类型相兼容、列的顺序不同等情况。这样就很容易现在备库上修改schema,然后将其提升为主库,减少停机时间。基于语句的复制一般允许更为灵活的操作。
基于语句的方式执行复制的过程基本上就是执行SQL语句。这意味着所有在服务器上发生的变更都以一种容易理解的方式允许。 - 基于语句的复制模式的缺点
很多情况下通过基于语句的模式无法正确的复制,几乎每一个安装的备库都会至少碰到一次,事实上对于存储过程、触发器以及其他的一些语句的复制在 5.0 和5.1 的一系列版本中存在大量的bug。这些语句复制的方式已经被修改了很多次,以使其更好的工作。简单的说,如果正在使用触发器和存储,就不要使用基于语句的复制模式。 - 基于行的复制的优点
几乎没有基于行的复制模式无法处理的场景。对于所有的SQL构造、触发器、存储过程等都能正确执行。只是当你试图做一些诸如备库正在修改表的 schema 这样的事情时才能导致复制失败。
这样方式同样可能减少锁的使用,因为它并不要求这种强串行化是可重复的。
基于行的复制模式会记录数据变更,因此在二进制日志中记录的都是实际在主库上变化了的数据。另外在一些情况下基于行的二进制日志文件还会记录发生改变之前的数据,因此这可能有利于某些数据的恢复。
在很多情况下,由于无须像基于语句的复制那样需要为查询的建立执行计划并执行查询,因此基于行的复制占用更少的CPU。
- 基于行的复制的缺点
由于语句并没有在日志里记录,因此无法判断执行了哪些SQL,除了需要知道行的变化外,这在很多情况下也很重要。
使用一种完全不同的方式在备库进行数据变更——而不是执行SQL。事实上,基于行的变化更像是一个黑盒子,你无法知道服务器在做什么,并且没有很好的文档和解释。
四、复制文件
接下来,我们来看看复制会用到的文件。前面已经介绍了二进制日志文件和中继日志文件,其实还有其他的文件也会被用到。
mysql-bin.index
当在服务器上开启二进制日志时,同时会生成一个和二进制日志同名的但以 .index 作为后缀的文件,该文件用于记录磁盘上的二进制日志文件。这里的 index 并不是指表的索引,而是说这个文件的每一行包含了二进制文件的文件名。
mysql-relday-bin-index
这个文件是中继日志的索引文件,和 mysql-bin.index 的作用类似。
master.info
这个文件是用于保存备库连接到主库所需要的信息,格式为纯文本,不同的SQL版本,其记录的信息也可能不同。此文件不能删除,否则无法连接到主库。这个文件以文本的格式记录了复制用户的密码,因此要注意此文件的权限控制。
relay-log.info
这个文件包含了当前备库复制的二进制日志和中继日志坐标,同样不能删除。
五、发送复制事件到其他备库
log_slave_updates 选项可以让备库变成其他服务器的主库。在设置该选项后,MySQL会将其执行的事件记录到它自己的二进制日志文件中。这样它的备库就可以从其日中检索并执行事件。
在这种场景下,主库将数据更新事件写入二进制日志,第一个备库提取并执行这个事件,这时候一个事件的生命周期应该已经结束了,但由于设置了log_slave_updates ,备库会将这个事件写入自己的二进制日志文件中。这样第二个备库就可以将事件提取到它的中继日志中并执行。默认这个选项是打开的,这样在连接到备库时就不用重启服务器了。
六、复制过滤器
复制过滤器选项允许仅复制服务器上的一部分数据。有两种复制过滤方式:在主库上过滤记录到二进制日志文件中的事件,以及在备库上过滤记录到中继日志的事件。
可以使用 binlog_do_db 和 binlog_ignore_db来 控制过滤,但通常不需要开启。因为这样不仅会破坏复制,还可能会导致从某个时间点的备份进行数据恢复时失败。
在备库上,可以通过设置 replicate_* 选项,在从中继日志中读取事件时进行过滤。可以复制或忽略一个或者多个数据库,把一个数据库重写到另一个数据库,或使用类似的 LIKE 的模式复制或忽略数据库表。
更好的办法是阻止一些特殊语句被复制,通常是设置 SQL_LOG_BIN = 0, 虽然这种方法也有它的缺点。总的来说,不要使用复制过滤,因为它很容易中断复制并导致问题,在需要灾难恢复时也会带来极大的不方便。