数据库集群时的master和slave同步相关方法

问: master还在运行中,如何在不停止它的情况下配置slave?

答: 需要设计几个选项参数。如果已经有了master的备份并且记录了数据快照二进制日志文件名以及偏移位置(运行

SHOW MASTER STATUS

查看结果),执行以下步骤:

  1. 确定slave指定了一个唯一的服务器编号。
  2. 在slave上执行如下语句,把一些选项值改成实际值:

    mysql> CHANGE MASTER TO -> MASTER_HOST='master_host_name', -> MASTER_USER='master_user_name', -> MASTER_PASSWORD='master_pass', -> MASTER_LOG_FILE='recorded_log_file_name', -> MASTER_LOG_POS=recorded_log_position;

  3. 在slave上执行

    START SLAVE

    语句。

如果事先没有备份master的数据,可以用以下方法快速创建一个备份。以下所有的操作都是在master上。

  1. 提交语句:

    mysql> FLUSH TABLES WITH READ LOCK;

  2. 确保这个锁一直存在,执行以下命令(或者其他类似的):

    shell> tar zcf /tmp/backup.tar.gz /var/lib/mysql

  3. 执行以下语句,记录下输出的结果,后面要用到:

    mysql> SHOW MASTER STATUS;

  4. 释放锁:

    mysql> UNLOCK TABLES;

上述步骤的另一个办法是创建master的SQL转储文件。只需在master上执行

mysqldump --master-data

命令,然后将导出来的SQL转储文件载入slave。不过,这么做会制作二进制数据快照的方式慢一点。

无论使用上述两种方法的哪种,最后都能创建master的数据快照然后记录二进制日志文件名以及偏移位置。可以在好几的其他的slave上使用同一个备份的二进制数据快照。得到master的快照后,只要master的二进制日志完好无损,接着就能开始设置slave了。两个决定是否需要等待较长时间的限制是:在master上磁盘空间保存二进制日志,以及slave从master抓取更新事件。

也可以使用

LOAD DATA FROM MASTER

。这个语句可以很方便地在slave上取得数据快照并且能立刻调整二进制日志文件名以及偏移位置。在将来,我们推荐用

LOAD DATA FROM MASTER

来设置slave。警告,它只能用于

MyISAM

表,并且可能会保持一个较长时间的读锁。由于它还没达到所期望的高效率,因此如果数据表很大,最好还是在执行完

FLUSH TABLES WITH READ LOCK

后直接制作二进制数据快照。

问:是否slave总是需要连接到master?

答:不,非必需。slave可以好几小时甚至几天关闭或者不连接master,然后重连再取得更新操作日志。例如,可以在拨号链接上设置一个mater/slave关系,拨号可能只是零星的不定期的连接。这种做法隐含的是,在任何指定的时间里,除非使用特殊的度量标准,否则slave不能保证总是能和master保持同步。在未来,有个选项可以阻止master,除非至少有一个slave在同步中。

问:怎么知道比master晚了多少?也就是说,怎么知道slave最后同步的时间?

答:如果slave是4.1.1或者更高,只需查看

SHOW SLAVE STATUS

结果中的

Seconds_Behind_Master

字段。对于老版本,可以用以下办法。如果在slave上执行

SHOW PROCESSLIST

语句结果显示SQL线程(对MySQL 3.23则是slave线程)正在运行,这就意味着该线程至少从master读取一个更新操作事件。详情请看"6.3 Replication Implementation Details"。

当SQL线程执行一个master上读取的更新操作事件时,它把自己的时间改成事件的时间(这也就是

TIMESTAMP

也要同步的原因)。在

SHOW PROCESSLIST

结果中的

Time

字段中,slave的SQL线程显示的秒数就是最后一次同步的时间戳和slave本机的实际时间相差秒数。可以根据这个值来判断最后同步的时间。注意,如果slave已经从master断开好几个小时了,然后重新连接,就能看到slave的

SHOW PROCESSLIST

结果中的SQL线程的

Time

字段的值类似3600。这是因为slave正在执行一个小时前的语句。

问:如何强制master在slave赶上全部更新之前阻止更新操作?

答:执行以下步骤:

  1. 在master上,执行以下语句:

    mysql> FLUSH TABLES WITH READ LOCK;mysql> SHOW MASTER STATUS;

    记录下结果中的日志文件名以及偏移位置,它们是同步的坐标值。

  2. 在slave上,提交以下语句,

    MASTER_POS_WAIT()

    函数的参数的值就是前面取得的同步坐标值:

    mysql> SELECT MASTER_POS_WAIT('log_name', log_offset);

    SELECT

    语句会阻止更新,直到slave同步到了上述日志文件及位置。在这个时候,slave就和master保持同步了,并且这个语句就会返回。

  3. 在master上,执行以下语句允许master重新处理更新操作:

    mysql> UNLOCK TABLES;

问:设置一个双向复制时要注意什么问题?

答:MySQL同步目前还不支持任何在master和slave上的分布式(跨服务器)更新锁协议以保证操作的原子性。也就是说,存在这样的可能性:客户端A在并存的master 1上做了一个更新,同时,在它同步到并存master 2上之前,客户端B在master 2上可能也做了一个和客户端A在master 1上不同的更新操作。因此,当客户端A所做的更新同步到master 2时,它将产生和master 1上不同的数据表,尽管master 2上的更新操作也全都同步到master 1上去。这意味着除非能确保所有的更新都能以任何顺序安全地执行,否则不要使用双向同步,或者除非注意在客户端程序中的不知原因的无序更新操作。

同时也要意识到在所关心的更新问题上,双向同步实际上并不能很大地改善性能(甚至没有)。两个服务器都需要执行同样数量的更新操作,在一台服务器上也是。唯一区别的是,可能这样做会减少一些锁争夺,因为来自其他服务器的更新操作都会被串行地放到slave线程中。甚至这种好处还可以作为网络延迟的补偿。

问:我如何利用同步来提高系统性能?

答:需要安装一个服务器作为master并且把所有的写操作直接放在这上面。然后配置多个廉价的使用机架磁盘的slave,把读操作分配给master和slave。还可以在启动slave时使用

--skip-innodb

,

--skip-bdb

,

--low-priority-updates

,和

--delay-key-write=ALL

选项来提高slave端的性能。这种情况下,slave会使用非事务的

MyISAM

表来代替

InnoDB

BDB

表,已取得更快速度。

问:如何准备客户端应用程序的代码来适应同步应用?

答:如果代码中负责存取数据库的部分已经被合理地抽象化/模块化了,将它们转化成适用运行于同步环境中将会很平滑和简单。只需要修改数据库存取实现部分,把所有的写操作放到master上,把所有的读操作放到master或者slave上。如果你的代码还没达到这个层次的抽象化,那么这将成为整理代码的机会和动机。可以使用类似以下函数创建封装类库或者模块:

  • safe_writer_connect()

  • safe_reader_connect()

  • safe_reader_statement()

  • safe_writer_statement()

每个函数名的

safe_

表示它们会处理所有的错误情况。可以使用其他函数名。重要的是,要为读连接、写连接、读、写定义好统一的接口。

然后将客户端代码转换成使用封装的类库。已开始可能是很痛苦且麻烦的,不过在将来长期运行中就能得到回报了。所有使用上述方法的应用程序都会在master/slave配置中有优势,即使包含多个slave。这些代码将很容易维护,一些额外的麻烦也会很少。自豪需要修改一个或者两个函数;例如,想要记录每个语句执行了多长时间,或者在上千个语句中哪个出现错误了。

如果已经写了很多代码,你可能想要自动转换它们,那么可以使用MySQL发布的

replace

工具,或者自己写转换脚本。理想地,你的代码已经使用了统一的编程风格。如果不是,最好重写它们,或者可以遍历检查一下,手工规范化一下代码风格。

问:MySQL同步何时且有多少能提高系统性能?

答:MySQL同步对于频繁读但不频繁写的系统很有好处。理论上来讲,使用单一master/多slave的配置,就可以通过这个方法来衡量系统:增加更多的slave直到用完所有的网络带宽或者master的更新操作增长到了不能再处理的点了。

想要知道增加多少个slave之后得到的性能才能平稳,以及能提高多少性能,就需要知道查询模式,并且根据经验对典型的master和slave做读(每秒读或

max_reads

)和写(

max_write

)基准测试得到它们之间的关系。下例展示了一个理想系统取得的性能的简单计算方法。

设定系统负载由10%写和90%读组成,我们已经通过基准测试确定

max_reads

是1200 - 2 *

max_writes

。换句话说,系统可以达到每秒做没有写的1200次读操作,写操作平均是读操作的2倍慢,它们之间的关系是线性的。让我们假设master和每个slave都有同样的容量,有一个master和N个slave。每个服务器(master或slave):

reads = 1200 - 2 * writes

reads = 9 * writes / (N + 1)

(读是分开的,但是所有写是在所有的服务器上的)

9 * writes / (N + 1) + 2 * writes = 1200

writes = 1200 / (2 + 9/(N+1))

最后的等式说明了N个slave的最大写数量,给它每分钟的最高读频率1200和1次写9次读的机率。

分析结论比率如下:

  • 如果 N = 0(意味着没有同步),系统大致可以处理每秒 1200/11 = 109 次写。
  • 如果 N = 1,增加到每秒 184 次写。
  • 如果 N = 8,增加到每秒 400 次写。
  • 如果 N = 17,增加到每秒 480 次写。
  • 最终,随着N接近无穷大(我们的预算为负无穷大),则可以达到几乎每秒 600 次写,大约提高系统吞吐量 5.5 倍。尽管如此,当有8台服务器时,已经提高了4倍了。

注意,上面的计算是假设了网络带宽无穷大,并且忽略了一些系统中比较大的因素。在很多情况下,当系统增加 N 个同步slave之后,是无法精确计算出上述预计结果的。不过,先看看下列问题将有助于你知道是否有和有多少系统性能上的改善:

  • 系统读/写得比率是多少?
  • 减少读操作后一个服务器能增加处理多少写操作?
  • 你的网络带宽足够给多少slave使用?

问:如何利用同步提供冗余/高可用性?

答:使用当前已经可用的特性,可以配置一个master和一个(或多个)slave,并且写一个脚本监控master是否运行着。然后通知应用程序和slave在发现错误时修改master。一些建议如下:

  • 使用

    CHANGE MASTER TO

    语句告诉slave修改master。

  • 一个让应用程序定位master所在主机的办法就是给master使用动态DNS。例如bind就可以用 `nsupdate` 来动态更新DNS。
  • 使用

    --log-bin

    选项,不使用

    --log-slave-updates

    选项来启动slave。这样就能让slave运行

    STOP SLAVE

    ;

    RESET MASTER

    语句后随时准备变成master,并且在其他slave上运行

    CHANGE MASTER TO

    。例如,有以下配置方案:

    WC / v WC----> M / | / / | / v v v S1 S2 S3

    M 表示masetr,S 表示slave,WC表示提交读写操作的客户端;只提交读操作的客户端没有表示出来,因为它们无需切换。S1,S2,S3都是使用

    --log-bin

    选项,不用

    --log-slave-updates

    选项运行的slave。由于除非指定

    --log-slave-updates

    参数,否则从master读到的更新操作都不会记录到二进制日志中,因此每个slave上的二进制日志都是空的。如果因为某些原因 M 不能用了,可以指定一个slave作为master。例如,如果指定S1,则所有的WC都要重定向到S1上,S2和S3都需要从S1上同步。

    确定所有的slave都已经处理完各自的中继日志了。在每个slave上,提交

    STOP SLAVE IO_THREAD

    语句,然后检查

    SHOW PROCESSLIST

    的结果直到看到

    Has read all relay log

    了。当所有的slave都这样子之后,就可以按照新的方案设置了。在slave S1上提交

    STOP SLAVE

    RESET MASTER

    语句将其提升为master。

    在其他slave S2和S3上,提交

    STOP SLAVE

    CHANGE MASTER

    TO MASTER_HOST='S1'

    (

    'S1'

    代表S1的真实主机名) 语句修改master。把S2,S3如何连接到S1的参数(用户,密码,端口等)都附加到

    CHANGE MASTER

    后面。在

    CHANGE MASTER

    中无需指定S1的二进制日志文件名和偏移位置:因为

    CHANGE MASTER

    默认就是第一个二进制日志和偏移位置4。最后,在S2和S3上提交

    START SLAVE

    语句。

    然后让所有的WC都把他们的语句重定向到S1上。从这个时候开始,从所有的WC发送到S1上的更新语句都会写到S1的二进制日志中,它们包含了从M死掉之后发送到S1的全部更新语句。

    配置结果如下:

    WC / | WC | M(unavailable) / | / | v v S1<--S2 S3 ^ | +-------+

    当M又起来了之后,只需在M上提交和在S2和S3上的一样的

    CHANGE MASTER

    语句,将它变成一个slave并且读取自从它死掉之后的全部WC提交的更新操作。想要把M重新变成master(例如因为它的性能更好),就执行类似上面的操作,把S1当作失效了,把M提升为新的master。在这个步骤中,别忘了在把S2和S3修改成为M的slave之前在M上运行

    RESET MASTER

    语句。否则的话,它们会从M开始失效的那个时刻开始读取WC提交的更新操作日志。

现在我们就运行着一个完整的自动选择master的MySQL同步系统,不过在它准备好之前,需要创建自己的监控工具。

6.10 同步疑难解答

如果按照上述步骤设定好同步之后,它不能正常工作的话,首先检查以下内容:

  • 查看一下错入日志信息。不少用户都在这方面做得不够好以至于浪费时间。
  • master是否在记录二进制日志?用

    SHOW MASTER STATUS

    检查一下状态。如果是,

    Position

    的值不为零;否则,确定master上使用了

    log-bin

    server-id

    选项。

  • slave是否运行着?运行

    SHOW SLAVE STATUS

    语句检查

    Slave_IO_Running

    Slave_SQL_Running

    的值是否都是。如果不是,确定是否用同步参数启动slave服务器了。

  • 如果slave正在运行,它是否建立了到master的连接?运行

    SHOW PROCESSLIST

    语句检查I/O和SQL线程的

    State

    字段值。详情请看"6.3 Replication Implementation Details"。如果I/O线程状态为

    Connecting to master

    ,就检查一下master上同步用户的权限是否正确,master的主机名,DNS设置,master是否确实正在运行着,以及slave是否可连接到master,等等。

  • 如果slave以前运行着,但是现在停止了,原因通常是一些语句在master上能成功但在slave上却失败了。如果salve已经取得了master的全部快照,并且除了slave线程之外不会修改他的数据,那么应该不会发生这样的情形。如果确实发生了,那么可能是一个bug或者你碰到了"6.7 Replication Features and Known Problems"中提到的同步限制之一。如果是一个bug,那么请按照"6.11 Reporting Replication Bugs"的说明报告它。
  • 如果一个语句在master上成功了,但是在slave上却失败了,并且这时不能做一次完整的数据库再同步(也就是删除slave上的数据,重新拷贝master的快照),那么试一下:
    1. 判断slave的数据表是否和master的不一样。试着找到怎么会发生这种情况,然后将slave的表同步成和master一样之后运行

      START SLAVE

    2. 如果上述步骤不生效或者没有执行,试着这个语句是否能被手工安全地运行(如果有必要),然后忽略master的下一个语句。
    3. 如果决定要忽略master的下一个语句,只需在slave上提交以下语句:

      mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER = n;mysql> START SLAVE;

      如果下一个语句没有使用

      AUTO_INCREMENT

      LAST_INSERT_ID()

      ,那么

      n

      的值应为为

      1

      。否则,它的值为

      2

      。设定为

      2

      是因为

      AUTO_INCREMENT

      LAST_INSERT_ID()

      在master的二进制日志中占用了2条日志。

    4. 如果确定slave精确地同步master了,并且没有除了slave线程之外的对数据表的更新操作,则推断这是因为bug产生的差异。如果是使用最近的版本,请报告这个问题,如果使用的是旧版本,试着升级一下。

6.11 报告同步Bugs

当确定没有包含用户的错误,并且同步还是不能正常工作或者不稳定,就可以报告bug了。我们需要你尽量多的跟踪bug的信息。请花点时间和努力准备一个好的bug报告。

如果有一个演示bug的可重现测试案例的话,请进入我们的bug数据库http://bugs.mysql.com/。如果碰到一个幽灵般的问题(不可重现),请按照如下步骤:

  1. 确定没有包含用户错误。例如,在slave线程以外更新slave的数据,那么数据就会不能保持同步,也可能会导致违反更新时的唯一键问题。这是外部干涉导致同步失败的问题。
  2. 使用

    --log-slave-updates

    --log-bin

    选项启动slave。这会导致slave将从master读取的更新操作写到自己的二进制日志中。

  3. 在重设同步状态之前保存所有的证据。如果我们没有任何信息或者只有粗略的信息,这将很难或者不可能追查到这个问题。需要收集以下证据:
    • master上的所有二进制日志
    • slave上的所有二进制日志
    • 发现问题时,在master上执行

      SHOW MASTER STATUS

      的结果

    • 发现问题时,在master上执行

      SHOW SLAVE STATUS

      的结果

    • 记录master和slave的错误日志
  4. mysqlbinlog

    来检查二进制日志。例如,用以下方法有助于找到有问题的查询:

    shell> mysqlbinlog -j pos_from_slave_status / /path/to/log_from_slave_status | head

一旦收集好了问题的证据,首先将它隔离到一个独立的测试系统上。然后在我们的bug数据库http://bugs.mysql.com/上进可能详细地报告问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值