第6章:MySQL中的复制 / 6.7. 复制特性和已知问题

一般原则,SQL级复制兼容性要求主服务器和从服务器均支持使用的特性。例如,在MySQL 5.0.0中开始使用TIMESTAMPADD()函数。如果在主服务器上使用该函数,不能复制到MySQL 5.0.0之前的从服务器。如果你计划在5.1和以前版本的MySQL之间进行复制,你应查阅对应以前版本系列的MySQL参考手册,查询该系列复制特征相关信息。

下面列出了关于支持什么和不支持什么的详细信息。关于复制的其它InnoDB具体信息参见15.2.6.5节,“InnoDB和MySQL复制”

关于保存的程序和触发器的复制问题在20.4节,“存储子程序和触发程序的二进制日志功能”中讨论。

·         AUTO_INCREMENTLAST_INSERT_ID()TIMESTAMP值正确实现复制。

·         USER()UUID()LOAD_FILE()函数毫无改变地被,这样不能可靠地在从服务器上工作。

·         下面的限制只适合基于语句的复制,而不是基于行的复制。处理用户级锁定的函数GET_LOCK()RELEASE_LOCK()IS_FREE_LOCK()IS_USED_LOCK()复制时从服务器不知道在主服务器上同时进行的相关文本;因此如果从服务器上的内容不同,这些函数不用来插入到主服务器的表中(例如不执行INSERT INTO mytable VALUES(GET_LOCK(...)))

·         MySQL 5.1FOREIGN_KEY_CHECKSSQL_MODEUNIQUE_CHECKSSQL_AUTO_IS_NULL变量均复制。但TABLE_TYPE,即STORAGE_ENGINE变量 不复制,有利于在不同的存储引擎之间进行复制。

·         即使主服务器和从服务器有不同的全局字符集变量,以及即使有不同的全局时区变量仍可以复制。

·         下面适合使用不同字符集的MySQL服务器之间的复制:

1.    必须在主服务器和从服务器上总是使用相同的全局字符集和校对规则(--default-character-set--default-collation)。否则,会在从服务器上遇到复制键值错误,因为在主服务器的字符集中被认为是唯一的键值在从服务器的字符集中可能不是唯一的。

2.    如果主服务器早于MySQL 4.1.3,则会话中的字符集不应与其全局值不同(换句话说,不要使用SET NAMESSET CHARACTER SET等等),因为从服务器不知道该字符集的更改。如果主服务器和从服务器均为4.1.3或更新版,可以随便将会话的字符集变量设置为本地值(例如NAMESCHARACTER SETCOLLATION_CLIENTCOLLATION_SERVER),因为这些设定值被写入二进制日志,因此从服务器知道。然而,禁止更改会话中这些变量的全局值;如前面所述,主服务器和从服务器必须具有唯一的全局字符集值。

3.    如果在主服务器上的数据库的字符集与全局collation_server值不同,则应设计CREATE TABLE语句,以便它们不隐含依赖数据库的默认字符集(Bug #2326);一个好的解决办法是在CREATE TABLE中明显说明字符集和校对规则。

·         应在主服务器和从服务器上设置相同的系统时区。否则一些语句,例如使用NOW()FROM_UNIXTIME()函数的语句,将不会正确复制。可以使用脚本mysqld_safe--timezone=timezone_name选项或通过设置TZ环境变量设置MySQL服务器运行的系统的时区。主服务器和从服务器还应有相同的默认连接时区设置;即主服务器和从服务器应有相同的--default-time-zone参数值。

·         CONVERT_TZ(...,...,@global.time_zone)不能正确复制。只有主服务器和从服务器均为5.0.4或更新版才能正确复制CONVERT_TZ(...,...,@session.time_zone)

·         会话变量只有在更新表的语句中使用时才能正确复制;例如:SET MAX_JOIN_SIZE=1000INSERT INTO mytable VALUES(@MAX_JOIN_SIZE)不能将相同的数据插入到主服务器上和从服务器上。不适用于通用的SET TIME_ZONE=...INSERT INTO mytable VALUES(CONVERT_TZ(...,...,@time_zone))

·         可以将从服务器上的非事务表复为主服务器上的事务表。例如,可以将主服务器上的InnoDB表复制为从服务器上的MyISAM表。然而,复制过程中,如果从服务器在BEGIN/COMMIT块过程中停止则会产生问题,因为从服务器在BEGIN块开始时会重启。该问题出现在TODO中,不久将会得到修复。

·         MySQL 5.1中可以正确复制引用用户变量(@var_name形式的变量)的更新语句;但在4.1以前的版本中却不可能。请注意从MySQL 5.1开始对用户变量名的大小写不再敏感;当在5.1和旧版本之间设置复制时应考虑该问题。

·         从服务器可以使用SSL连接到主服务器。

·         有一个全局系统变量slave_transaction_retries:如果因为某个InnoDB死锁或超过 InnoDBinnodb_lock_wait_timeoutNDB簇的TransactionDeadlockDetectionTimeoutTransactionInactiveTimeoutREPLICATION SLAVESQL线程未能执行某个事务,在给出错误停止前自动重试slave_transaction_retries次。 默认值是10。从MySQL 5.0.4开始,可以从SHOW STATUS的输出中看到重试总次数;参见5.3.4节,“服务器状态变量”

·         如果在主服务器上的CREATE TABLE语句中使用了DATA DIRECTORYINDEX DIRECTORY子句,子句也可以在从服务器上使用。如果在从服务器主机文件系统中不存在一致的目录或虽然存在但不能被从服务器访问,则会带来问题。MySQL 5.1支持一个称为NO_DIR_IN_CREATEsql_mode选项。如果从服务器运行时将SQL模式设置为包括该选项,复制CREATE TABLE语句时将忽略这些子句。结果是在表的数据库目录中创建了MyISAM数据和索引文件。

·         下面的限制只适合基于语句的复制,而不是基于行的复制:如果在查询中数据修改不确定,主服务器和从服务器上的数据可以不同;也就是由查询优化器确定。(这是常用的但不是很好的习惯,即使不是在复制中也不好)关于该问题的详细解释,参见A.8.1节,“MySQL中的打开事宜”

·         READ LOCKFLUSH LOGSFLUSH MASTERFLUSH SLAVEFLUSH TABLES不记入日志,因为如果复制到从服务器会造成问题。关于语法示例,参见13.5.5.2节,“FLUSH语法”FLUSH TABLESANALYZE TABLEOPTIMIZE TABLEREPAIR TABLE语句被写入二进制日志并会复制到从服务器。一般情况不会造成问题,因为这些语句不修改表的数据。但是在某些情况下会带来问题。如果你复制mysql数据库中的授权表并且不使用GRANT直接更新那些表,必须在从服务器上执行FLUSH PRIVILEGES使新的权限生效。并且,如果使用FLUSH TABLES重新命名MERGE表的MyISAM表,必须手动在从服务器上执行FLUSH TABLES。如果不指定NO_WRITE_TO_BINLOG或其别名LOCAL,则这些语句被写入二进制日志。

·         MySQL只支持一个主服务器和多个从服务器。我们计划将来添加一个投票算法,当前的主服务器出现问题时自动切换。我们还计划引入代理过程通过向不同的从服务器发送SELECT查询以帮助进行负载均衡。

·         当服务器关闭、重启时,其MEMORY表将变为空。主服务器按下述方法复制该结果:启动后第1次主服务器使用每个MEMORY表,它通知从服务器需要向表写入DELETE FROM语句来清空二进制日志的表。详细信息参见15.4节,“MEMORY (HEAP)存储引擎”

·         除了关闭从服务器(而不仅仅是从服务器线程) 临时表都被复制,并且还没有在从服务器上执行的更新所使用的临时表也已经复制。如果关闭从服务器,从服务器重启后更新需要的那些临时表不可再用。为了避免该问题,临时表打开时不要关闭从服务器。而应遵照下面的程序:

1.    执行STOP SLAVE语句。

2.    使用SHOW STATUS检查slave_open_temp_tables变量的值。

3.    如果值为0,使用mysqladmin shutdown命令关闭从服务器。

4.    如果值不为0,用START SLAVE重启从服务器线程。

5.    后面再重复该程序看下次的运气是否好一些。

我们计划在不久的将来修复该问题。

·         可以很安全地连接用--logs-slave-updates选项指定的循环主服务器/从服务器关系中的服务器。但请注意许多语句在这种设置中不能正确工作,除非你的客户代码关注了潜在的在不同的服务器不同顺序的更新中可能发生的这类问题。

这说明你可以象这样创建设置:

A -> B -> C -> A

服务器ID被编码在二进制日志事件中,因此服务器A知道何时自己首次创建它读取的事件并且不执行事件(除非用--replicate-same-server-id选项启动了服务器A,只在很少情况下有意义)。这样,没有无限循环。只有对表执行没有冲突的更新时该类循环设置才能工作。换句话说,如果在AC中插入数据,绝对不应在A中插入键值可能与插入到C中的行相冲突的一行。如果更新的顺序很重要,还不应更新两个服务器上相同的行。

·         如果从服务器上的某个语句产生错误,则从服务器上的SQL线程终止,并且从服务器向错误日志写入一条消息。此时应手动连接从服务器,修复该问题(例如,一个不存在的表),然后运行START SLAVE

·         可以很安全地关闭主服务器并在以后重启。如果某个从服务器丢失与主服务器的连接,从服务器尝试立即重新连接。如果失败,从服务器定期重试。(默认设置是每60秒重试一次。可以通过--master-connect-retry选项更改)从服务器也能够处理网络连接中断。但是,只有从服务器超过slave_net_timeout秒没有从主服务器收到数据才通知网络中断。如果中断时间短,可以降低slave_net_timeout。参见5.3.3节,“服务器系统变量”

·         关闭从服务器(净关闭)也很安全,因为它可以跟踪它离开的地点。不纯净的关闭操作会产生问题,特别是系统关闭前硬盘缓存未刷新到硬盘上时。如果有不间断电源,可以大大提高系统容错能力。不纯净的关闭主服务器会造成主服务器上的表和二进制日志内容之间的不一致性;在主服务器上使用InnoDB表和--innodb-safe-binlog选项可以避免该问题。参见5.11.3节,“二进制日志”(注释:MySQL 5.1中不需要--innodb-safe-binlog,由于引入了XA事务支持已经作废了)

·         由于MyISAM表的非事务属性,可以有一个语句只是更新一个表并返回错误代码。例如,多行插入时有一个行超过键值约束,或者如果长的更新语句更新部分行后被杀掉了。如果发生在主服务器上,除非错误代码合法并且语句执行产生相同的错误代码,从服务器线程将退出并等待数据库管理员决定如何做。如果该错误代码验证行为不理想,可以用--slave-skip-errors选项掩盖(忽视)部分或全部错误。

·         如果从BEGIN/COMMIT系列的非事务表更新事务表,如果提交事务前更新非事务表,对二进制日志的更新可能会不同步。这是因为事务提交后才被写入二进制日志。

·         事务混合更新事务表和非事务表时,二进制日志中语句的顺序是正确的,即使在ROLLBACK时,所有需要的语句也会写入二进制日志。但是如果在第1个连接的事务完成前,第2个连接更新非事务表,语句记入日志时会出现顺序错误,因为第2个连接的更新执行完后立即写入日志,而不管第1个连接执行的事务的状态如何。