复制文件
mysql-bin.index
- 当在服务器上开启二进制日志时,同时会生成一个和二进制日志同名的但以.index作为后缀的文件,该文件用于记录磁盘上的二进制日志文件。这里的“index” 并不是指表的索引,而是说这个文件的每一行包含了二进制文件的文件名。 你可能认为这个文件是多余的,可以被删除(毕竞MySQL可以在磁盘上找到它需要的文件)。事实上并非如此,MySQL依赖于这个文件,除非在这个文件里有记录, 否则MySQL识别不了二进制日志文件。
mysql-relay-bin-index
- 这个文件是中继日志的索引文件,和mysql-bin.index的作用类似。
master.info
- 这个文件用于保存备库连接到主库所需要的信息,格式为纯文本(每行一个值),不 同的MySQL版本,其记录的信息也可能不同。此文件不能删除,否则备库在重启后无法连接到主库。这个文件以文本的方式记录了复制用户的密码,所以要注意此 文件的权限控制。
relay-log.info
- 这个文件包含了当前备库复制的二进制日志和中继日志坐标(例如,备库复制在主 库上的位置),同样也不要删除这个文件,否则在备库重启后将无法获知从哪个位置开始复制,可能会导致重放已经执行过的语句。
使用这些文件来记录MySQL复制和日志状态是一种非常粗糙的方式。更不幸的是,它们不是同步写的。如果服务器断电并且文件数据没有被刷新到磁盘,在重启服务器后, 文件中记录的数据可能是错误的。正如之前提到的这些问题在MySQL5.5里做了改进。
以.index作为后缀的文件也与设置expire_logs_days 存在交互,该参数定义了MySQL 清理过期日志的方式,如果文件mysql-bin.index在磁盘上不存在,在某些MySQL版本自动清理就会不起作用,甚至执行PURGE MASTER LOGS 语句也没有用。这个问题的解决 法通常是使用MySQL服务器管理二进制日志,这样就不会产生误解(这意味着不应该使用rm来自己清理日志)
最好能显式地执行一些日志清理策略,比如设置expire_logs_days参数或者其他方式, 否则MySQL的二进制日志可能会将磁盘撑满。当做这些事情时,还需要考虑到备份策略。
发送复制事件到其他备库
log_slave_updates选项可以让备库变成其他服务器的主库。在设置该选项后,MySQL会将其执行过的事件记录到它自己的二进制日志中。这样它的备库就可以从其日志中检索并执行事件。下图述了这一过程。
在这种场景下,主库将数据更新事件写人二进制日志,第一个备库提取并执行这个事件。 这时候一个事件的生命周期应该已经结束了,但由于设置了log_slave_updates, 备库 会将这个事件写到它自己的二进制日志中。这样第二个备库就可以将事件提取到它的中 继日志中并执行。这意味着作为源服务器的主库可以将其数据变化传递给没有与其直接相连的备库上。默认情况下这个选项是被打开的,这样在连接到备库时就不需要重启服务器。
当第一个备库将从主库获得的事件写人到其二进制日志中时,这个事件在备库二进制日 志中的位置与其在主库二进制日志中的位置几乎肯定是不相同的,可能在不同的日志文件或文件内不同的位置。这意味着你不能假定所有拥有同一逻辑复制点的服务器拥有相同的日志坐标。稍后我们会提到,这种情况会使某些任务更加复杂,例如,修-一个备库的主库或将备库提升为主库。
除非你已经注意到要给每个服务器分配一个唯一的服务器ID,否则按照这种方式配置备库会导致一些奇怪的错误,甚至还会导致复制停止。一个更常见的问题是:为什么要指定服务器ID,难道MySQL在不知道复制命令来源的情况下不能执行吗?为什么MySQL要在意服务器ID是全局唯一的。问题的答案在于MySQL在复制过程中如何防止无限循环。当复制SQL线程读中继日志时,会丢弃事件中记录的服务器ID和该服务器本身ID相同的事件,从而打破了复制过程中的无限循环。在某些复制拓扑结构下打破无限循环非常重要,例如主-主复制结构。
复制和容量规划
写操作通常是复制的瓶颈,并且很难使用复制来扩展写操作。当计划为系统增加复制容量时,需要确保进行了正确的计算,否则很容易犯一些复制相关的错误。
例如,假设工作负载为20%的写以及80%的读。为了计算简单,假设有以下前提:
读和写查询包含同样的工作量。
所有的服务器是等同的,每秒能进行1000次查询。
备库和主库有同样的性能特征。
可以把所有的读操作转移到备库。
如果当前有一个服务器能支持每秒1000次查询,那么应该增加多少备库才能处理当前两倍的负载,并将所有的读查询分配给备库?
看上去应该增加两个备库并将1600次读操作平分给它们。但是不要忘记,写入负载同样增加到了400次每秒,并且无法在主备服务器之间进行分摊。每个备库每秒必须处理400次写入,这意味着每个备库写入占了40%,只能每秒为600次查询提供服务。因此, 需要三台而不是两台备库来处理双倍负载。
如果负载再增加一倍呢?将有每秒800次写入,这时候主库还能处理,但备库的写入同样也提升到80%,这样就需要16台备库来处理每秒3200次读查询。并且如果再增加一点负载,主库也会无法承担。
这远远不是线性扩展,查询数量增加4倍,却需要增加17倍的服务器。这说明当为单台主库增加备库时,将很快达到投入远高于回报的地步。这仅仅是基于上面的假设,还忽略了一些事情,例如,单线程的基于语句的复制常常导致备库容量小于主库。真实的复制配置比我们的理论计算还要更差。
为什么复制无法扩展写操作
糟糕的服务容量比例的根本原因是不能像分发读操作那样把写操作等同地分发到更多服 务器上。换句话说,复制只能扩展读操作,无法扩展写操作。
你可能想知道到底有没有办法使用复制来增加写入能力。答案是否定的,根本不行。对数据进行分区是唯一可以扩展写入的方法。
可能会想到使用主-主拓扑结构并为两个服务器执行写操作。这种配置比主备结构能支持稍微多一点的写入, 因为可以在两台服务器之间共享串行化带来的开销。如果每台服务器上执行50%的写入,那复制的执行量也只有50%需要串行化。理论上讲,这比在一台机器上(主库)对100%的写入并发执行,而在另外一台机器(备库)上对100%的写入做串行化要更优。
这可能看起来很吸引人,然而这种配置还比不上单台服务器能支持的写入。一个有50%的写入被串行化的服务器性能比一台全部写入都并行化的服务器性能要低。
这是这种策略不能扩展写入的原因。它只能在两台服务器间共享串行化写入的缺点。所以“链中最弱的一环”并不是那么弱,它只提供了比主动-被动复制稍微好点的性能, 但是增加了很大的风险,通常不能带来任何好处。
备库什么时候开始延迟
一个关于备库比较普遍的问题是如何预测备库会在何时跟不上主库。很难去描述备库使用的复制容量为5%与95%的区别,但是至少能够在接近饱和前预警并估计复制容量。
首先应该观察复制延迟的尖刺。如果有复制延迟的曲线图,需要注意到图上的一些短的延迟骤升,这时候可能负载加大,备库短时间内无法跟上主库。当负载接近耗尽备库的容量时,会发现曲线上的凸起会更高更宽。前面曲线的上升角度不变,但随后当备库在产生延迟后开始追赶主库时,将会产生一个平缓的斜坡。这些突起的出现和增长是一 个警告信息,意味着已经接近容量限制。
为了预测在将来的某个时间点会发生什么,可以人为地制造延迟,然后看多久备库能赶上主库。目的是为了明确地说明曲线上的斜坡的陡度。如果将备库停止一个小时,然后开启并在1小时内追赶上,说明正常情况下只消耗了一半的容量。也就是说,如果中午12:00停止备库复制,在1:00开启,并且在2:00追赶上,备库在一小时内完成了两个小 时内所有的变更,说明复制可以在双倍速度下运行。
最后,如果使用的是Percona Server或者MariaDB,也可以直接获取复制的利用率。打 开服务器变量userstat,然后执行如下语句:
WHEREUSER='#mysq1_system#'\G
****************** 1.row*********
USER:#mysql_system#
TOTALCONNECTIONS:1
CONCURRENTCONNECTIONS:2
CONNECTEDTIME:46188
BUSYTIME:719
ROWSFETCHED:0
ROWSUPDATED:1882292
SELECTCOMMANDS:0
UPDATECONMANDS:580431
OTHER_CONMANDS:338857
CONMIT_TRANSACTIONS:1016571
ROLLBACKTRANSACTIONS:0
可以将BUSY_ IME和CONNECTED_TIME 的一半(因为备库有两个复制线程)做比较,来观察备库线程实际执行命令所花费的时间。在例子里,备库大约使用了其3%的能力,这并不意味着它不会遇到偶然的延迟尖刺--如果主库运行了一个超过10分钟才完成的变更,可能延迟的时间和变更执行的时间是相同的----但这很好地暗示了备库 能够很快从一个延迟尖刺中恢复。