MySQL 数据库复制

作者:Mike Diehl

直到最近,我一直是 PostgreSQL 数据库的忠实粉丝,但 MySQL 的数据库复制实现非常出色。

我不想过多地贬低我最喜欢的 RDBMS,但 PostgreSQL 的复制机制似乎都有些笨拙。例如,我对在所有表上创建更新/删除触发器不感兴趣。MySQL 的机制对客户端完全透明,并且不需要任何表定义更改;这一切都在服务器端完成。

当我提到“数据库复制”时,我不是指批量文件传输。我也不是指将更新/删除语句发送到多个数据库服务器的代理机制。我指的是能够在不对客户端进行任何额外干预的情况下,在一个服务器上进行的数据库更新反映在多个其他服务器上的能力。所有这一切都发生在我所谓的“近实时”中。必然存在一些传播延迟。

通过实施复制(实际上,为了利用它,我将整个应用程序迁移到了 MySQL),我试图减轻对我的应用程序以及我的业务的两种潜在威胁:完整的服务器或服务故障,以及例行备份周期期间的性能下降。如果我的主数据库服务器发生故障,我可以简单地将我的应用程序指向另一个复制服务器并继续开展业务。在我的情况下,我只需移动一个 DNS 指针即可完成此任务。另一方面,由于数据库转储会对我的服务器造成负载,我目前在深夜执行数据库转储。通过复制,我可以在黄金时段在本地只读服务器上执行每日备份。我的主服务器永远不需要知道它们已被备份。

因此,在讨论如何设计和实施 MySQL 复制之前,让我们简要地谈谈它的工作原理。本质上,MySQL 使用主从模型,其中主服务器保留其执行的所有数据库更新的日志。然后,一个或多个从服务器连接到主服务器,读取每个日志条目,并执行指示的更新。主服务器跟踪内务处理问题,例如日志轮换和访问控制。每个从服务器都必须维护其在服务器事务日志中的当前位置的概念。当服务器上发生新事务时,它们会被记录在主服务器上并由每个从服务器下载。一旦每个从服务器都提交了事务,从服务器就会更新它们在服务器事务日志中的位置并等待下一个事务。这一切都是异步完成的,这意味着主服务器不必等待从服务器“赶上”。这也意味着,如果从服务器在一段时间内无法连接到主服务器,它只需在重新建立连接时下载所有待处理的事务即可。到目前为止,我发现它非常稳定。

即使我们讨论的是主从模型,我们实际上也可以构建相当多的不同服务器拓扑来适应不同的需求。

基本情况是我们有两个服务器,并且简单地将一个服务器从属于另一个服务器,这导致了主从配置。在这种情况下,我们可以将从服务器视为热备用服务器,以防主服务器发生故障,或者是在其上运行耗时报告而不影响主服务器的服务器。通过添加从服务器,我们可以实现星型拓扑。

如果我们只有两个服务器,我们可以简单地将每个服务器配置为另一个服务器的从服务器,我们最终会得到一个运行良好的主主配置。在一个服务器上发生的事务会反映在另一个服务器上,反之亦然。在这种情况下,每个服务器都与另一个服务器完全等效,因此您可以将此配置用作负载均衡机制。

这些主题的变体使我们能够构建复制数据库服务器的链甚至环,但这可能不如您想象的那么有利。例如,在链式拓扑中,每个服务器和链接都会增加数据传播时间以及额外的故障点。因此,虽然您可以构建一些真正令人惊叹的拓扑,但总的来说,您希望尽可能保持简单。对于我的特定情况,我选择了一个主主配置,其中一个额外的服务器从属于其中一个主服务器。此配置为我提供了热备用,并可以从故障中自动恢复。我还获得了在我的从服务器上运行备份的能力,或者在容纳其他两个服务器的数据中心失去连接的情况下,从从服务器运行我的整个业务的能力。对于如此简单的拓扑来说,这要求很高。

当我第一次着手在我的数据库服务器上配置复制时,我真的以为这将是一个复杂的过程;但事实并非如此。我的方法是完整阅读 MySQL 网站上的所有文档,在 MySQL 复制邮件列表上提出几个问题,然后开始工作。阅读文档所花费的时间比实际进行配置所花费的时间要长,这应该是理所当然的。我发现最有帮助的两个资源是:

dev.mysql.com/doc/refman/5.0/en/replication.html

www.howtoforge.com/mysql_database_replication

第一个链接是一份相当详尽的文档,详细介绍了所有配置问题和选项。我建议您在最终确定您的设计之前完整阅读它。第二个链接指向一个自称为“复制和粘贴”操作指南的文档。

因为我更感兴趣的是讨论一些陷阱,所以我只简要讨论配置过程。幸运的是,我们只需要修改 /etc/mysql/my.cnf 的 [mysqld] 部分,在 CLI 中发出几个 SQL 命令,然后重启 MySQL。这是我添加到我的一个主服务器的内容

#skip-networking
bind-address=0.0.0.0
log-bin=mysql-bin
server-id=11
innodb_flush_log_at_trx_commit=1
sync_binlog=1
auto_increment_offset=2
auto_increment_increment=5
master-host=master2.example.com
master-user=slave1
master-password=password1

以上所有项目在前面提到的资源中都有详细记录,因此我不会在此处赘述。但是,auto_increment_offset 和 auto_increment_increment 项目有点有趣。

在主主配置中,完全有可能在两个服务器上的同一张表上发生插入。如果该表中的一个字段是自增字段,会发生什么?问题是我们希望这样的字段具有在服务器之间唯一的值,但我们实际上没有一种机制让服务器执行此簿记工作。因此,我们使用了一个巧妙的解决方法;我们不是每次在带有自增字段的表上执行插入时都加一,而是加一个更大的数字,比如 5,再加上一个偏移量。然后,每个主服务器将被分配一个唯一的偏移量,该偏移量小于增量值。例如,我们可以对我们的两个主服务器使用 5 的增量和 1 和 2 的偏移量。然后,当我们插入到自增字段时,我们的一个服务器将分配值 1、6、11、16、21...,而另一个服务器将使用值 2、7、12、17、22...。没有一个服务器会分配另一个服务器可能分配的值。在这种情况下,我们将跳过值 3、4、5、8、9、10 等,但我们将保证我们的每个值都是唯一的。如果我们然后添加更多的主服务器,我们可以为它们分配不同的偏移量,只要我们拥有的服务器少于 5 个。

前面提到的资源详细介绍了其余的必要步骤,所以我将只简要列出这些步骤。

设置复制的下一步是授予从服务器连接到主服务器以下载事务日志的权限。然后我们必须找出主服务器上事务日志中的当前位置,并告知每个从服务器此位置,以便它们可以从当前事务开始。然后我们重启 MySQL,为了安全起见,我重启了我的所有服务器,这就是我所要做的。我花了不到 30 分钟就完成了它。

一旦您使复制工作起来,您需要注意一些事项。最大的问题是如何将数据放入您新复制的数据库中。显然,从一个空数据库开始,然后开始添加数据要容易得多。不幸的是,我的数据库中已经有数据了。网站上有程序详细介绍了几种方法,具体取决于您可以承受多少停机时间。我只是转储并删除了我的所有数据库,配置了复制,并在其中一个主服务器上恢复了数据。这对我有用,但有更好的方法。

正如我之前所说,PostgreSQL 是我最喜欢的 RDBMS,但复制是迁移到 MySQL 的一个非常令人信服的理由。我发现复制非常容易配置,到目前为止,它一直非常稳定。

加载 Disqus 评论