科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道MySQL手册版本 5.0.20-MySQL同步(三)

MySQL手册版本 5.0.20-MySQL同步(三)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

最早的二进制格式是在MySQL 3.23中开发出来的。在MySQL 4.0中改进了,MySQL 5.0又改进了。在配置同步时需要升级服务器的话,它们之间的因果关系在\"6.6 Upgrading a Replication Setup\"中描述了。

来源:LUPA 2008年5月22日

关键字: 数据库 技巧 MySQL

  • 评论
  • 分享微博
  • 分享邮件

现在讨论使用不同字符集的MySQL服务器间同步的问题。

首先,在master和slave上必须总是使用同样的全局字符集以及校验字符集(--default-character-set, --default-collation 都是相关的全局变量)。否则,slave上可能会出现键重复(duplicate-key)的错误,因为用master的字符集认为该键可能是唯一的,但是用slave的字符集则未必然。

第二,如果master必须低于MySQL 4.1.3,则会话(session)的字符集必须和全局值一样(也就是说,不能执行 SET NAMES, SET CHARACTER SET 等语句),因为这些对字符集的修改在slave不能识别。如果master是4.1.3或者更高,slave也是这样的话,那么会话字符集就可以随便修改了(执行 NAMES, CHARACTER SET, COLLATION_CLIENT, COLLATION_SERVER 等),并且这些修改都会被记录到二进制日志中,然后同步到slave上,它就知道怎么做了。该会话还会阻止试图修改这些全局变量的操作;就如前面所说, master和slave必须使用同样的全局字符集。

如果在master上有和全局变量 collation_server 不一样字符集的数据库,那么就要设计 CREATE TABLE 语句使得数据表不隐式地使用该数据库的默认字符集,因为这目前还是一个bug(Bug #2326);一个变通的办法是在 CREATE TABLE 语句中显式地声明数据表的字符集以及校验字符集。

有时可能会把master上的事务表同步到slave后变成非事务表。例如,可以在slave上把master的 InnoDB 表当成 MyISAM 表。不过,slave在一个 BEGIN/COMMIT 区块中停止的话就有问题了,因为slave会从 BEGIN 重新开始。这个问题已经放到TODO中,很快会被修复。

更新语句中如果用到了用户自定义变量(例如变量 @var_name)的情况下,在MySQL 3.23和4.0不能被正确同步。在 4.1 这已经修复了。注意,从MySQL 5.0开始,用户变量就不区分大小写了。在做MySQL 5.0和旧版本间的同步需要考虑到这个问题。

从4.1.1以及更高版本中,slave可以用SSL方式连接到master。

在master 上执行的 CREATE TABLE 语句如果包括了 DATA DIRECTORY或 INDEX DIRECTORY 子句,那么它也会应用于slave上。如果slave上不存在对应的目录或者没有权限时便出现问题。从MySQL 4.0.15开始,有个 sql_mode 选项叫 NO_DIR_IN_CREATE。如果slave的SQL模式包含这个选项,那么它在同步 CREATE TABLE 语句前会忽略前面提到的2个子句。结果就是 MyISAM 的数据和索引文件都只能放在该表的数据库目录下。

尽管没听说过发生过类似的情况,不过理论上确实存在这种可能性:如果一个查询被设计为非确定方式的修改数据,那么可能导致master和slave的数据不一致。那么,就把决定的权力交给查询优化器吧。(通常这不是一个好的做法,甚至超出了同步的范围,详情请看"1.8.7.3 Open Bugs and Design Deficiencies in MySQL")

在MySQL 4.1.1之前,FLUSH, ANALYZE TABLE, OPTIMIZE TABLE,和 REPAIR TABLE 语句没有写入到二进制日志中,因此也不会同步到slave上。这通常不会引发问题,因为它们并没有修改数据。不过在特定情况下可能导致问题。如果同步 mysql 数据库下的权限表,在更新时不是用 GRANT 语句,那么必须在slave上执行那么必须在slave上执行 FLUSH PRIVILEGES 语句才能使之生效。同样地,如果还有一个 MyISAM 表是 MERGE 表的一部分,那么必须在slave上手工执行 FLUSH TABLES 语句。从MySQL 4.1.1开始,这些语句都写入二进制日志了(除非指定选项 NO_WRITE_TO_BINLOG 或它的同名选项 LOCAL)。一些例外的情况是 FLUSH LOGS, FLUSH SLAVE, 和 FLUSH TABLES WITH READ LOCK (它们中的任何一个同步到slave的话都可能导致问题)。例子可见"14.5.4.2 FLUSH Syntax"。

MySQL只支持一个master多个slave的机制。以后我们会增加一个表决算法,如果当前master出现问题时能自动切换。同时也会引进一个"代理"进程来帮助将 SELECT 查询发送到不同的slave上达到负载均衡。

当服务器关闭,重启后,所有的 MEMORY (HEAP) 表都清空了。从MySQL 4.0.18开始,master用以下方式同步它们:一旦master开始使用一个 MEMORY 表,它会在用完这些表之后在二进制日志中写入一个 DELETE FROM 语句告诉slave把它们删除。详情请看"15.3 The MEMORY (HEAP) Storage Engine"。

除非关闭slave(只是关闭slave线程),临时表也会同步;并且在slave上已经记录了一些还未被执行的需要用到临时表的更新语句。关闭slave再重启后更新所需的临时表就不复存在了。为了避免这个问题,在有临时表时就不要关闭slave。或者,使用以下步骤:

提交一个 STOP SLAVE 语句。

使用 SHOW STATUS 语句检查变量 Slave_open_temp_tables 的值。

如果它的值是0,运行 mysqladmin shutdown 命令关闭slave。

如果它的值不是0,用 START SLAVE 语句重启slave线程。

如果还有这样的好运气就再次执行同样的步骤吧。^_^

我们会尽快解决这个问题。

如果在一个循环master/slave同步关系中指定 --log-slave-updates 选项,那么就可以安全地连接到各个服务器上。注意,很多语句可能在这种设置环境下不能正常工作,除非程序中已经特别注意避免这种更新时潜在的问题了,因为可能在不同服务器上不同的顺序上发生更新问题。这意味着可以设定像下面的循环:

A -> B -> C -> A

服务器ID都已经编码到二进制日志中了,因此服务器A知道那些自己创建的日志,从而不会去执行它们(除非在服务器A上启动时增加 --replicate-same-server-id 选项,这个选项只在极少数情况下设置有意义)。因此,这就不会存在无限循环了。不过这个循环只有在更新表时没有发生冲突才不会发生问题。换言之,如果在A 和C中同时插入一条记录,那么可能在A中不可能插入数据,因为它的键可能跟C的键冲突了。同样地,也不能在两个服务器上更新同一条记录,除非2次更新操作间有足够的时间间隔。

如果在slave上执行一个SQL语句后产生错误,那么slave的SQL线程就终止,然后它在错误日志中写入一条信息。可以连接到slave上,解决问题(例如,不存在表)后,运行 START SLAVE 语句重启它。

可以放心地关闭master(干净地)之后再重启它。如果slave到master的连接断开了,它会立刻重连。如果失败了,slave会定期重试(默认是每60秒重试一次,可通过 --master-connect-retry 选项来修改)。slave也会处理网络断开的情况。不过,slave会在 slave_net_timeout 秒之后如果还没收到来自master的数据才会当作网络断开的情况来处理。如果断开时间不长,可以减少 slave_net_timeout 的值。详情请看"5.2.3 Server System Variables"。

也可以放心地关闭slave(干净地),它会记录停止的地方。不干净地关闭slave可能产生问题,特别是系统关闭了但缓存还没刷新到磁盘时。可以提供不间断电源来提高系统容错性。master的不干净关闭可能导致表和二进制内容的不一致;如果是 InnoDB 表,使用 --innodb-safe-binlog 选项在master上就能避免这个问题。详情请看"5.9.4 The Binary Log"。

由于 MyISAM 表的非事务本质,就可能发生一个语句只更新了部分表就返回错误代码的情况。例如,一个多重插入语句中,有一条记录违反了约束键规则,一个更新语句在更新了一些记录后辈杀掉了。如果在master上发生这种情况了,那么slave线程会推出,等待数据库管理员决定要怎么做,除非这个错误代码是合法的并且这个语句的执行结果也是一样的错误代码。并没有关于错误代码是否合法的详细描述,一些错误代码可以用 --slave-skip-errors 选项屏蔽掉。这个选项从MySQL 3.23.47开始就可以用了。

如果把非事务表同步到事务表时在一个 BEGIN/COMMIT 段内更新数据表了,如果在非事务表提交之前有其他线程更新它了,那么这个更新操作就不会正确地同步到二进制日志中。这是因为只有整个事务成功提交了才会写到二进制日志中。

在4.0.15 之前,任何在非事务表的更新操作会在它执行的时候立刻写入到二进制日志中,然而事务表的更新只有在 COMMIT 后才写入,ROLLBACK 的话就不写入了。因此在一些事务中更新事务表或非事务表时就需要考虑这个情况了(不只是同步时会碰到这个问题,想要把二进制日志作为备份时也一样)。在 MySQL 4.0.15中,我们已经修改了更新事务和非事务表混合的情况下的日志记录行为,它解决了这个问题(对于二进制日志来说顺序地记录语句是比较不错的做法,所有必须的语句都会写进去,ROLLBACK 也一样)。当第二个连接更新非事务表而第一个连接的事务还没结束时,就会有同样的问题了;仍会发记录语句顺序发生错误的问题,因为第二个连接会在更新完成后立刻写入到日志中。

当4.x 的slave从3.23的master上同步 LOAD DATA INFILE 时,SHOW SLAVE STATUS 中的 Exec_Master_Log_Pos 和 Relay_Log_Space 字段的值就不正确了。Exec_Master_Log_Pos 值不正确的话在重启slave之后会导致问题;因此最好在重启前修改一下这个值,只需在master上运行 FLUSH LOGS。这个bug在MySQL 5.0.0的slave中已经解决了。

下表列出了MySQL 3.23同步时会发生的问题,它们在MySQL 4.0已经解决了:

LOAD DATA INFILE 能正确处理,只要那个数据文件在更新开始时仍然存在于master上。

LOAD DATA LOCAL INFILE 不再像以前3.23那样被略过了。

在3.23中,RAND() 更新同步不正常。因此在使用 RAND() 更新时采用 RAND(some_non_rand_expr) 格式。例如,可以用 UNIX_TIMESTAMP() 作为 RAND() 的参数。

在3.23中,RAND() 更新同步不正常。因此在使用 RAND() 更新时采用 RAND(some_non_rand_expr) 格式。例如,可以用 UNIX_TIMESTAMP() 作为 RAND() 的参数。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章